1 ;;; etags.el --- etags facility for Emacs
3 ;; Copyright 1985, 1986, 1988, 1990, 1997, 2003 Free Software Foundation, Inc.
5 ;; Author: Their Name is Legion (see list below)
6 ;; Maintainer: XEmacs Development Team
9 ;; This file is part of XEmacs.
11 ;; XEmacs is free software; you can redistribute it and/or modify it
12 ;; under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; XEmacs is distributed in the hope that it will be useful, but
17 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 ;; General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with XEmacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
26 ;;; Synched up with: Not synched with FSF.
30 ;; This file is completely different from FSF's etags.el. It appears
31 ;; that an early version of this file (tags.el) has been rewritten by
32 ;; two different people; we got one, FSF got the other. Various
33 ;; people have said that our version is better and faster.
38 ;; Derived from the original lisp/tags.el.
40 ;; Ideas and code from the work of the following people:
41 ;; Andy Norman <ange@hplb.hpl.hp.com>, author of ange-tags.el
42 ;; Ramana Rao <rao@arisia.xerox.com>
43 ;; John Sturdy <jcgs@harlqn.co.uk>, author of tags-helper.el
44 ;; Henry Kautz <kautz@allegra.att.com>, author of tag-completion.el
45 ;; Dan LaLiberte <liberte@cs.uiuc.edu>, author of local-tags.el
46 ;; Tom Dietterich <tgd@turing.cs.orst.edu>, author of quest.el
47 ;; The author(s) of lisp/simple.el
48 ;; Duke Briscoe <briscoe@cs.yale.edu>
49 ;; Lynn Slater <lrs@indetech.com>, author of location.el
50 ;; Shinichirou Sugou <shin@sgtp.apple.juice.or.jp>
51 ;; an unidentified anonymous elisp hacker
52 ;; Kyle Jones <kyle_jones@wonderworks.com>
53 ;; added "Exact match, then inexact" code
54 ;; added support for include directive.
55 ;; Hrvoje Niksic <hniksic@xemacs.org>
62 "Etags facility for Emacs.
63 Using etags, you can create tag tables for any number of files, and
64 easily access the symbols in those files, using the `\\[find-tag]'
70 (defcustom tags-build-completion-table 'ask
71 "*If this variable is nil, then tags completion is disabled.
72 If it is t, then things which prompt for tags will do so with completion
73 across all known tags.
74 If it is the symbol `ask', you will be asked whether each tags table
75 should be added to the completion list as it is read in. (With the
76 exception that for very small tags tables, you will not be asked,
77 since they can be parsed quickly.)"
78 :type '(choice (const :tag "Disabled" nil)
79 (const :tag "Complete All" t)
80 (const :tag "Ask" ask))
83 (defcustom tags-always-exact nil
84 "*If this variable is non-nil, then tags always looks for exact matches.
85 If it is nil (the default), tags will first go through exact matches,
86 then through the non-exact ones."
90 (defcustom tag-table-alist nil
91 "*A list which determines which tags files are active for a buffer.
92 This is not really an association list, in that all elements are
93 checked. The CAR of each element of this list is a pattern against
94 which the buffer's file name is compared; if it matches, then the CDR
95 of the list should be the name of the tags table to use. If more than
96 one element of this list matches the buffer's file name, then all of
97 the associated tags tables will be used. Earlier ones will be
100 If the CAR of elements of this list are strings, then they are treated
101 as regular-expressions against which the file is compared (like the
102 auto-mode-alist). If they are not strings, then they are evaluated.
103 If they evaluate to non-nil, then the current buffer is considered to
106 If the CDR of the elements of this list are strings, then they are
107 assumed to name a TAGS file. If they name a directory, then the string
108 \"TAGS\" is appended to them to get the file name. If they are not
109 strings, then they are evaluated, and must return an appropriate string.
112 (setq tag-table-alist
113 '((\"/usr/src/public/perl/\" . \"/usr/src/public/perl/perl-3.0/\")
114 (\"\\\\.el$\" . \"/usr/local/emacs/src/\")
115 (\"/jbw/gnu/\" . \"/usr15/degree/stud/jbw/gnu/\")
116 (\"\" . \"/usr/local/emacs/src/\")
119 This means that anything in the /usr/src/public/perl/ directory should use
120 the TAGS file /usr/src/public/perl/perl-3.0/TAGS; and file ending in .el should
121 use the TAGS file /usr/local/emacs/src/TAGS; and anything in or below the
122 directory /jbw/gnu/ should use the TAGS file /usr15/degree/stud/jbw/gnu/TAGS.
123 A file called something like \"/usr/jbw/foo.el\" would use both the TAGS files
124 /usr/local/emacs/src/TAGS and /usr15/degree/stud/jbw/gnu/TAGS (in that order)
125 because it matches both patterns.
127 If the buffer-local variable `buffer-tag-table' is set, then it names a tags
128 table that is searched before all others when find-tag is executed from this
131 If there is a file called \"TAGS\" in the same directory as the file in
132 question, then that tags file will always be used as well (after the
133 `buffer-tag-table' but before the tables specified by this list.)
135 If the variable tags-file-name is set, then the tags file it names will apply
136 to all buffers (for backwards compatibility.) It is searched first."
137 :type '(repeat (cons :format "%v"
139 (regexp :tag "Buffer regexp")
142 (string :tag "Tag file or directory")
146 (defvar buffer-tag-table nil
147 "*The additional name of one TAGS table to be used for this buffer.
148 You can set this with `\\[set-buffer-tag-table]'. See the documentation
149 for the variable `tag-table-alist' for more information.")
150 (make-variable-buffer-local 'buffer-tag-table)
152 (defvar tags-file-name nil
153 "The name of the tags-table used by all buffers.
154 This is for backwards compatibility, and is largely supplanted by the
155 variable tag-table-alist.")
157 (defcustom tags-auto-read-changed-tag-files nil
158 "*If non-nil, always re-read changed TAGS file without prompting.
159 If nil, prompt whether to re-read the changed TAGS file."
163 (defcustom make-tags-files-invisible nil
164 "*If non-nil, TAGS-files will not show up in buffer-lists or be
165 selectable (or deletable.)"
169 (defcustom tags-search-nuke-uninteresting-buffers t
170 "*If non-nil, keep newly-visited files if they contain the search target.
171 This affects the `tags-search' and `tags-query-replace' commands."
175 (defcustom tags-check-parent-directories-for-tag-files t
176 "*If non-nil, look for TAGS files in all parent directories."
180 (defcustom tags-exuberant-ctags-optimization-p nil
181 "*If this variable is nil (the default), then exact tag search is able
182 to find tag names in the name part of the tagtable (enclosed by ^?..^A)
183 and in the sourceline part of the tagtable ( enclosed by ^..^?).
184 This is needed by xemacs etags as not every tag has a name field.
185 It is slower for large tables and less precise than the other option.
187 If it is non-nil, then exact tag will only search tag names in the name
188 part (enclosed by ^?..^A). This is faster and more precise than the other
189 option. This is only usable with exuberant etags, as it has a name field
190 entry for every tag."
194 ;; Buffer tag tables.
196 (defun buffer-tag-table-list ()
197 "Returns a list (ordered) of the tags tables which should be used for
200 ;; Explicitly set buffer-tag-table
201 (when buffer-tag-table
202 (push buffer-tag-table result))
204 (when (file-readable-p (concat default-directory "TAGS"))
205 (push (concat default-directory "TAGS") result))
206 ;; Parent directories
207 (when tags-check-parent-directories-for-tag-files
208 (let ((cur default-directory))
209 ;; Fuck! Shouldn't there be a more obvious portable way
210 ;; to determine if we're the root? Shouldn't we have a
211 ;; proper path manipulation API? Do you know how many
212 ;; god-damn bugs are lurking out there because of Unix/
213 ;; Windows differences? And how much code is littered
214 ;; with stuff such as 10 lines down from here?
215 (while (not (and (equal (file-name-as-directory cur) cur)
216 (equal (directory-file-name cur) cur)))
217 (setq cur (expand-file-name ".." cur))
218 (let ((parent-tag-file (expand-file-name "TAGS" cur)))
219 (when (file-readable-p parent-tag-file)
220 (push parent-tag-file result))))))
222 (let* ((key (or buffer-file-name
223 (concat default-directory (buffer-name))))
224 (key (if (eq system-type 'windows-nt)
225 (replace-in-string key "\\\\" "/")
228 (dolist (item tag-table-alist)
229 (setq expression (car item))
230 ;; If the car of the alist item is a string, apply it as a regexp
231 ;; to the buffer-file-name. Otherwise, evaluate it. If the
232 ;; regexp matches, or the expression evaluates non-nil, then this
233 ;; item in tag-table-alist applies to this buffer.
234 (when (if (stringp expression)
235 (string-match expression key)
238 ;; Now evaluate the cdr of the alist item to get the name of
239 ;; the tag table file.
240 (setq expression (ignore-errors
242 (if (stringp expression)
243 (push expression result)
244 (error "Expression in tag-table-alist evaluated to non-string")))))
245 (setq result (buffer-tag-table-list-load result))
246 (setq result (delq nil result))
247 ;; If no TAGS file has been found, ask the user explicitly.
248 ;; #### tags-file-name is *evil*.
249 (or result tags-file-name
250 (call-interactively 'visit-tags-table))
252 (setq result (nconc result (buffer-tag-table-list-load (list tags-file-name)))))
253 (or result (error "Buffer has no associated tag tables"))
254 (delete-duplicates (nreverse result) :test 'equal)))
256 (defun buffer-tag-table-list-load (list &optional used-buffers)
257 "Load all tag buffers in LIST. Include directives inside the tag
258 Buffers result in a recursive call off this function. The USED-BUFFERS
259 parameter is just for internal use and prevents infinite inclusion
260 loops. The return value is a list of loaded buffers with the order
261 from LIST preserved. The tag files loaded with the include directive
262 are inserted into the returned list before their parents."
267 (when (file-directory-p name)
268 (setq name (concat (file-name-as-directory name) "TAGS")))
270 (file-readable-p name)
272 (set-buffer (get-tag-table-buffer name))
273 (when (not (member buffer-file-name used-buffers))
274 (add-to-list 'used-buffers buffer-file-name)
275 (let ((include-files (tag-table-include-files)))
277 (setq result (nconc result
278 (buffer-tag-table-list-load
279 include-files used-buffers)))))
280 (add-to-list 'result buffer-file-name t)))))
285 (defun visit-tags-table (file)
286 "Tell tags commands to use tags table file FILE when all else fails.
287 FILE should be the name of a file created with the `etags' program.
288 A directory name is ok too; it means file TAGS in that directory."
289 (interactive (list (read-file-name "Visit tags table: (default TAGS) "
291 (expand-file-name "TAGS" default-directory)
293 (if (string-equal file "")
294 (setq tags-file-name nil)
295 (setq file (expand-file-name file))
296 (when (file-directory-p file)
297 (setq file (expand-file-name "TAGS" file)))
298 ;; It used to be that, if a user pressed RET by mistake, the bogus
299 ;; `tags-file-name' would remain, causing the error at
300 ;; `buffer-tag-table'.
301 (when (file-exists-p file)
302 (setq tags-file-name file))))
304 (defun set-buffer-tag-table (file)
305 "In addition to the tags tables specified by the variable `tag-table-alist',
306 each buffer can have one additional table. This command sets that.
307 See the documentation for the variable `tag-table-alist' for more information."
310 (read-file-name "Visit tags table: (directory sufficient) "
311 nil default-directory t)))
312 (or file (error "No TAGS file name supplied"))
313 (setq file (expand-file-name file))
314 (when (file-directory-p file)
315 (setq file (expand-file-name "TAGS" file)))
316 (or (file-exists-p file) (error "TAGS file missing: %s" file))
317 (setq buffer-tag-table file))
320 ;; Manipulating the tag table buffer
322 (defconst tag-table-completion-status nil
323 "Indicates whether a completion table has been built.
324 Either nil, t, or `disabled'.")
325 (make-variable-buffer-local 'tag-table-completion-status)
327 (defconst tag-table-files nil
328 "If the current buffer is a TAGS table, this holds a list of the files
329 referenced by this file, or nil if that hasn't been computed yet.")
330 (make-variable-buffer-local 'tag-table-files)
332 (defun get-tag-table-buffer (tag-table)
333 "Returns a buffer visiting the given TAGS table.
334 If appropriate, reverting the buffer, and possibly build a completion-table."
335 (or (stringp tag-table)
336 (error "Bad tags file name supplied: %s" tag-table))
337 ;; Remove symbolic links from name.
338 (setq tag-table (symlink-expand-file-name tag-table))
339 (let (buf build-completion check-name)
340 (setq buf (get-file-buffer tag-table))
342 (if (file-readable-p tag-table)
343 (setq buf (find-file-noselect tag-table)
345 (error "No such tags file: %s" tag-table)))
346 (with-current-buffer buf
347 ;; Make the TAGS buffer invisible.
348 (when (and check-name
349 make-tags-files-invisible
350 (string-match "\\`[^ ]" (buffer-name)))
351 (rename-buffer (generate-new-buffer-name
352 (concat " " (buffer-name)))))
353 (or (verify-visited-file-modtime buf)
354 (cond ((or tags-auto-read-changed-tag-files
356 (format "Tags file %s has changed, read new contents? "
358 (when tags-auto-read-changed-tag-files
359 (message "Tags file %s has changed, reading new contents..."
362 (when (eq tag-table-completion-status t)
363 (setq tag-table-completion-status nil))
364 (setq tag-table-files nil))))
365 (or (eq (char-after 1) ?\f)
366 (error "File %s not a valid tags file" tag-table))
367 (or (memq tag-table-completion-status '(t disabled))
368 (setq build-completion t))
369 (when build-completion
370 (if (ecase tags-build-completion-table
374 ;; don't bother asking for small ones
375 (or (< (buffer-size) 20000)
377 (format "Build tag completion table for %s? "
379 ;; The user wants to build the table:
382 (if tags-exuberant-ctags-optimization-p
383 (add-to-tag-completion-table-exuberant-ctags)
384 (add-to-tag-completion-table))
385 (setq tag-table-completion-status t))
386 ;; Allow user to C-g out correctly
388 (message "Tags completion table construction aborted")
389 (setq tag-table-completion-status nil
392 ;; The table is verboten.
393 (setq tag-table-completion-status 'disabled))))
396 (defun file-of-tag ()
397 "Return the file name of the file whose tags point is within.
398 Assumes the tag table is the current buffer.
399 File name returned is relative to tag table file's directory."
400 (let ((opoint (point))
403 (goto-char (point-min))
404 (while (< (point) opoint)
407 (skip-chars-backward "^,\n")
409 size (read (current-buffer)))
412 ;; New include syntax
414 ;; tacked on to the end of a tag file means use filename
415 ;; as a tag file before giving up.
417 (unless (eq size 'include)
418 (forward-char size)))
419 (goto-char (1- prev))
420 (buffer-substring (point) (point-at-bol)))))
422 (defun tag-table-include-files ()
423 "Return all file names associated with `include' directives in a tag buffer."
424 ;; New include syntax
426 ;; tacked on to the end of a tag file means use filename as a
427 ;; tag file before giving up. The filenames are expanded to avoid
428 ;; problems with relative paths being used in the wrong directory.
431 (goto-char (point-min))
432 (while (re-search-forward tags-include-pattern nil t)
433 (push (expand-file-name (match-string 1)) files)))
436 (defun tag-table-files (tag-table)
437 "Returns a list of the files referenced by the named TAGS table."
438 (with-current-buffer (get-tag-table-buffer tag-table)
439 (unless tag-table-files
440 (let (files prev size)
441 (goto-char (point-min))
445 (skip-chars-backward "^,\n")
447 size (read (current-buffer)))
449 (push (expand-file-name (buffer-substring (1- (point))
455 (setq tag-table-files (nreverse files))))
458 ;; #### should this be on previous page?
459 (defun buffer-tag-table-files ()
460 "Returns a list of all files referenced by all TAGS tables that
463 (mapcar #'tag-table-files (buffer-tag-table-list))))
466 ;; Building the completion table
468 ;; Test cases for building completion table; must handle these properly:
469 ;; Lisp_Int, XSETINT, current_column
\7f60,2282
470 ;; Lisp_Int, XSETINT, point>NumCharacters ? 0 : CharAt(
\7f363,9935
471 ;; Lisp_Int, XSETINT, point<=FirstCharacter ? 0 : CharAt(
\7f366,10108
472 ;; point<=FirstCharacter || CharAt(
\7f378,10630
473 ;; point>NumCharacters || CharAt(
\7f382,10825
474 ;; DEFUN ("x-set-foreground-color", Fx_set_foreground_color,
\7f191,4562
475 ;; DEFUN ("x-set-foreground-color", Fx_set_foreground_color,
\7f191,4562
476 ;; DEFUN ("*", Ftimes,
\7f1172,32079
477 ;; DEFUN ("/=", Fneq,
\7f1035,28839
478 ;; defun_internal
\7f4199,101362
479 ;; int pure[PURESIZE / sizeof
\7f53,1564
480 ;; char staticvec1[NSTATICS * sizeof
\7f667,17608
481 ;; Date: 04 May 87 23:53:11 PDT
\7f26,1077
482 ;; #define anymacroname(
\7f324,4344
483 ;; (define-key ctl-x-map
\7f311,11784
484 ;; (define-abbrev-table 'c-mode-abbrev-table
\7f24,1016
485 ;; static char *skip_white(
\7f116,3443
486 ;; static foo
\7f348,11643
487 ;; (defun texinfo-insert-@code
\7f91,3358
488 ;; (defvar texinfo-kindex)
\7f29,1105
489 ;; (defun texinfo-format-\.
\7f548,18376
490 ;; (defvar sm::menu-kludge-y
\7f621,22726
491 ;; (defvar *mouse-drag-window*
\7f103,3642
492 ;; (defun simula-back-level(
\7f317,11263
493 ;; } DPxAC,
\7f380,14024
494 ;; } BM_QCB;
\7f69,2990
495 ;; #define MTOS_DONE\t
497 ;; "^[^ ]+ +\\([^ ]+\\) "
499 ;; void *find_cactus_segment(
\7f116,2444
500 ;; void *find_pdb_segment(
\7f162,3688
501 ;; void init_dclpool(
\7f410,10739
502 ;; WORD insert_draw_command(
\7f342,8881
503 ;; void *req_pdbmem(
\7f579,15574
505 (defvar tag-completion-table (make-vector 511 0))
508 (defvar tag-table-symbol)
509 (defvar tag-symbol-tables)
510 (defvar buffer-tag-table-list)
512 (defmacro intern-tag-symbol (tag)
514 (setq tag-symbol (intern ,tag tag-completion-table)
515 tag-symbol-tables (and (boundp tag-symbol)
516 (symbol-value tag-symbol)))
517 (or (memq tag-table-symbol tag-symbol-tables)
518 (set tag-symbol (cons tag-table-symbol tag-symbol-tables)))))
520 ;; Can't use "\\s-" in these patterns because that will include newline
521 ;; \2 matches an explicit name.
522 (defconst tags-explicit-name-pattern "\177\\(\\([^\n\001]+\\)\001\\)?")
523 ;; \1 matches Lisp-name, \2 matches C-name, \5 (from
524 ;; tags-explicit-name-pattern) matches explicit name.
525 (defconst tags-DEFUN-pattern
526 (concat "DEFUN[ \t]*(\"\\([^\"]+\\)\",[ \t]*\\(\\(\\sw\\|\\s_\\)+\\),"
527 tags-explicit-name-pattern))
528 ;; \1 matches an array name. Explicit names unused?
529 (defconst tags-array-pattern ".*[ \t]+\\([^ \[]+\\)\\[")
530 ;; \2 matches a Lispish name, \5 (from tags-explicit-name-pattern) matches
532 (defconst tags-def-pattern
533 (concat "\\(.*[ \t]+\\)?\\**\\(\\(\\sw\\|\\s_\\)+\\)[ ();,\t]*"
534 ;; "\\(.*[ \t]+\\)?\\(\\(\\sw\\|\\s_\\)+\\)[ ()]*"
535 ;; "\\(\\sw\\|\\s_\\)+[ ()]*"
536 tags-explicit-name-pattern)
538 ;; \1 matches Schemish name, \4 (from tags-explicit-name-pattern) matches
540 (defconst tags-schemish-pattern
541 (concat "\\s-*(\\s-*def\\sw*\\s-*(?\\s-*\\(\\(\\sw\\|\\s_\\|:\\)+\\))?\\s-*"
542 tags-explicit-name-pattern))
543 (defconst tags-file-pattern "^\f\n\\(.+\\),[0-9]+\n")
544 (defconst tags-include-pattern "^\f\n\\(.+\\),include\n"
545 "Holds the pattern for finding the include directive in tagfiles.")
548 (defun add-to-tag-completion-table-exuberant-ctags ()
549 "Sucks the current buffer (a TAGS table) into the completion-table.
550 This is a version which is optimized for exuberant etags and will not
551 work with xemacs etags."
552 (message "Adding %s to tags completion table..." buffer-file-name)
553 (goto-char (point-min))
554 (let ((tag-table-symbol (intern buffer-file-name tag-completion-table))
555 ;; tag-table-symbol is used by intern-tag-symbol
558 (case-fold-search nil))
559 (while (re-search-forward tags-explicit-name-pattern nil t)
560 ;; no need to check the mode here
561 (setq name (match-string 2))
562 (intern-tag-symbol name)))
563 (message "Adding %s to tags completion table...done" buffer-file-name))
566 (defun add-to-tag-completion-table ()
567 "Sucks the current buffer (a TAGS table) into the completion-table."
568 (message "Adding %s to tags completion table..." buffer-file-name)
569 (goto-char (point-min))
570 (let ((tag-table-symbol (intern buffer-file-name tag-completion-table))
571 ;; tag-table-symbol is used by intern-tag-symbol
572 filename file-type name name2 name3 tag-symbol
574 (case-fold-search nil))
575 ;; Loop over the files mentioned in the TAGS file for each file,
576 ;; try to find its major-mode, then process tags appropriately.
577 (while (looking-at tags-file-pattern)
578 (goto-char (match-end 0))
579 (setq filename (file-name-sans-versions (match-string 1))
580 ;; We used to check auto-mode-alist for the proper
581 ;; file-type. This was way too slow, as it had to process
582 ;; an enormous amount of regexps for each time. Now we
583 ;; use the shotgun approach with only two regexps.
584 file-type (cond ((string-match "\\.\\([cC]\\|cc\\|cxx\\)\\'"
587 ((string-match "\\.\\(el\\|cl\\|lisp\\)\\'"
590 ((string-match "\\.scm\\'" filename)
593 (defvar c-mode-syntax-table)
594 (set-syntax-table (cond ((and (eq file-type 'c-mode)
597 ((eq file-type 'lisp-mode)
598 lisp-mode-syntax-table)
599 (t (standard-syntax-table))))
600 ;; Clear loop variables.
601 (setq name nil name2 nil name3 nil)
602 (lmessage 'progress "%s..." filename)
603 ;; Loop over the individual tag lines.
604 (while (not (or (eobp) (eq (char-after) ?\f)))
605 (cond ((and (eq file-type 'c-mode)
606 (looking-at "DEFUN[ \t]"))
608 (or (looking-at tags-DEFUN-pattern)
609 (error "DEFUN doesn't fit pattern"))
610 (setq name (match-string 1)
611 name2 (match-string 2)
612 name3 (match-string 5)))
613 ;;((looking-at "\\s-")
614 ;; skip probably bogus entry:
616 ((and (eq file-type 'c-mode)
617 (looking-at ".*\\["))
619 (cond ((not (looking-at tags-array-pattern))
620 (message "array definition doesn't fit pattern")
623 (setq name (match-string 1)))))
624 ((and (eq file-type 'scheme-mode)
625 (looking-at tags-schemish-pattern))
626 ;; Something Schemish (is this really necessary??)
627 (setq name (match-string 1)
628 name2 (match-string 4)))
629 ((looking-at tags-def-pattern)
631 (setq name (match-string 2)
632 name2 (match-string 5))))
633 ;; add the tags we found to the completion table
634 (and name (intern-tag-symbol name))
635 (and name2 (intern-tag-symbol name2))
636 (and name3 (intern-tag-symbol name3))
638 ;; Skip over the include entries at the bottom of the file.
639 (while (looking-at tags-include-pattern)
640 (goto-char (match-end 0))
641 (setq filename (file-name-sans-versions (match-string 1)))
643 (or (eobp) (error "Bad TAGS file")))
644 (message "Adding %s to tags completion table...done" buffer-file-name))
647 ;; Interactive find-tag
649 (defvar find-tag-default-hook nil
650 "Function to call to create a default tag.
651 Make it buffer-local in a mode hook. The function is called with no
654 (defvar find-tag-hook nil
655 "*Function to call after a tag is found.
656 Make it buffer-local in a mode hook. The function is called with no
659 ;; Return a default tag to search for, based on the text at point.
660 (defun find-tag-default ()
661 (or (and (not (memq find-tag-default-hook '(nil find-tag-default)))
662 (with-trapping-errors
663 :function 'find-tag-default-hook
665 (funcall find-tag-default-hook)))
666 (symbol-near-point)))
668 ;; This function depends on the following symbols being bound properly:
669 ;; buffer-tag-table-list,
670 ;; tag-symbol-tables (value irrelevant, bound outside for efficiency)
671 (defun tag-completion-predicate (tag-symbol)
672 (and (boundp tag-symbol)
673 (setq tag-symbol-tables (symbol-value tag-symbol))
675 (while tag-symbol-tables
676 (when (memq (car tag-symbol-tables) buffer-tag-table-list)
678 (setq tag-symbol-tables (cdr tag-symbol-tables))))))
680 (defun buffer-tag-table-symbol-list ()
681 (mapcar (lambda (table-name)
682 (intern table-name tag-completion-table))
683 (buffer-tag-table-list)))
685 (defvar find-tag-history nil "History list for find-tag-tag.")
687 (defun find-tag-tag (prompt)
688 (let* ((default (find-tag-default))
689 (buffer-tag-table-list (buffer-tag-table-symbol-list))
690 tag-symbol-tables tag-name)
694 (format "%s(default %s) " prompt default)
696 tag-completion-table 'tag-completion-predicate nil nil
697 'find-tag-history default))
700 (defvar last-tag-data nil
701 "Information for continuing a tag search.
702 Is of the form (TAG POINT MATCHING-EXACT TAG-TABLE TAG-TABLE ...).")
704 (defvar tags-loop-operate nil
705 "Form for `tags-loop-continue' to eval to change one file.")
707 (defvar tags-loop-scan
708 '(error "%s" (substitute-command-keys
709 "No \\[tags-search] or \\[tags-query-replace] in progress."))
710 "Form for `tags-loop-continue' to eval to scan one file.
711 If it returns non-nil, this file needs processing by evalling
712 \`tags-loop-operate'. Otherwise, move on to the next file.")
714 (autoload 'get-symbol-syntax-table "symbol-syntax")
716 (defun find-tag-internal (tagname)
718 (let ((next (null tagname))
719 (tmpnext (null tagname))
720 ;; If tagname is a list: (TAGNAME), this indicates
721 ;; requiring an exact symbol match.
722 (exact (or tags-always-exact (consp tagname)))
723 (normal-syntax-table (syntax-table))
724 (exact-syntax-table (get-symbol-syntax-table (syntax-table)))
725 tag-table-currently-matching-exact
726 tag-target exact-tagname
727 tag-tables tag-table-point file linebeg line startpos buf
728 offset found pat syn-tab)
729 (when (consp tagname)
730 (setq tagname (car tagname)))
732 (setq tagname (car last-tag-data))
733 (setq tag-table-currently-matching-exact
734 (car (cdr (cdr last-tag-data)))))
736 (setq tag-table-currently-matching-exact t)))
737 ;; \_ in the tagname is used to indicate a symbol boundary.
738 (if tags-exuberant-ctags-optimization-p
739 (setq exact-tagname (format "\C-?%s\C-a" tagname))
740 (setq exact-tagname (format "\C-?%s\C-a\\|\
741 \\_%s.?\C-?[0-9]*,[0-9]*$" tagname tagname))
743 (while (string-match "\\\\_" exact-tagname)
744 (aset exact-tagname (1- (match-end 0)) ?b))
747 ;; Loop searching for exact matches and then inexact matches.
748 (while (not (eq tag-table-currently-matching-exact 'neither))
750 (setq tag-tables (cdr (cdr (cdr last-tag-data)))
751 tag-table-point (car (cdr last-tag-data)))
752 ;; Start from the beginning of the table list on the
753 ;; next iteration of the loop.
756 (setq tag-tables (buffer-tag-table-list)
758 (if tag-table-currently-matching-exact
759 (setq tag-target exact-tagname
760 syn-tab exact-syntax-table)
761 (setq tag-target tagname
762 syn-tab normal-syntax-table))
763 (with-search-caps-disable-folding tag-target t
765 (set-buffer (get-tag-table-buffer (car tag-tables)))
766 (bury-buffer (current-buffer))
767 (goto-char (or tag-table-point (point-min)))
768 (setq tag-table-point nil)
769 (letf (((syntax-table) syn-tab)
770 (case-fold-search nil))
771 ;; #### should there be support for non-regexp
773 (while (re-search-forward tag-target nil t)
774 (and (save-match-data
776 (goto-char (match-beginning 0))
777 (looking-at "[^\n\C-?]*\C-?")))
778 ;; If we're looking for inexact matches, skip
779 ;; exact matches since we've visited them
781 (or tag-table-currently-matching-exact
782 (letf (((syntax-table) exact-syntax-table))
784 (goto-char (match-beginning 0))
785 (not (looking-at exact-tagname)))))
787 (setq tag-tables (cdr tag-tables))))
788 (if (and (not exact) (eq tag-table-currently-matching-exact t))
789 (setq tag-table-currently-matching-exact nil)
790 (setq tag-table-currently-matching-exact 'neither)))
791 (error "No %sentries %s %s"
793 (if exact "matching" "containing")
797 ;; from here down, synched with FSF 20.7
798 ;; etags-snarf-tag and etags-goto-tag-location. --ben
804 ;; The match was for a source file name, not any tag
805 ;; within a file. Give text of t, meaning to go exactly
806 ;; to the location we specify, the beginning of the file.
811 (expand-file-name (file-of-tag)
812 ;; In XEmacs, this needs to be
814 (or (file-name-directory (car tag-tables))
816 (search-forward "\C-?")
818 (expand-file-name (file-of-tag)
819 ;; In XEmacs, this needs to be
821 (or (file-name-directory (car tag-tables))
823 (setq linebeg (buffer-substring (1- (point)) (point-at-bol)))
824 ;; Skip explicit tag name if present.
825 (search-forward "\001" (save-excursion (forward-line 1) (point)) t)
826 (if (looking-at "[0-9]")
827 (setq line (string-to-int (buffer-substring
829 (progn (skip-chars-forward "0-9")
832 (if (looking-at "[0-9]")
833 (setq startpos (string-to-int (buffer-substring
835 (progn (skip-chars-forward "0-9")
837 ;; Leave point on the next line of the tags file.
840 (nconc (list tagname (point) tag-table-currently-matching-exact)
842 (setq buf (find-file-noselect file))
844 ;; LINEBEG is the initial part of a line containing the tag and
845 ;; STARTPOS is the character position of LINEBEG within the file
846 ;; (starting from 1); LINE is the line number. If LINEBEG is t,
847 ;; it means the tag refers to exactly LINE or STARTPOS
848 ;; (whichever is present, LINE having preference, no searching).
849 ;; Either LINE or STARTPOS may be nil; STARTPOS is used if
850 ;; present. If the tag isn't exactly at the given position then
851 ;; look around that position using a search window which expands
852 ;; until it hits the start of file.
854 (with-current-buffer buf
860 (cond (line (goto-line line))
861 (startpos (goto-char startpos))
862 (t (error "etags.el BUG: bogus direct file tag")))
863 ;; Here we search for PAT in the range [STARTPOS - OFFSET,
864 ;; STARTPOS + OFFSET], with increasing values of OFFSET.
866 ;; We used to set the initial offset to 1000, but the
867 ;; actual sources show that finer-grained control is
868 ;; needed (e.g. two `hash_string's in src/symbols.c.) So,
869 ;; I changed 1000 to 100, and (* 3 offset) to (* 5 offset).
871 (setq pat (concat (if (eq selective-display t)
873 (regexp-quote linebeg)))
875 ;; The character position in the tags table is 0-origin.
876 ;; Convert it to a 1-origin Emacs character position.
877 (if startpos (setq startpos (1+ startpos)))
878 ;; If no char pos was given, try the given line number.
881 (setq startpos (progn (goto-line line)
884 (setq startpos (point-min)))
885 ;; First see if the tag is right at the specified location.
887 (setq found (looking-at pat))
888 (while (and (not found)
890 (goto-char (- startpos offset))
893 (re-search-forward pat (+ startpos offset) t)
894 offset (* 5 offset))) ; expand search window
895 ;; Finally, try finding it anywhere in the buffer.
897 (re-search-forward pat nil t)
898 (error "Rerun etags: `%s' not found in %s"
900 ;; Position point at the right place
901 ;; if the search string matched an extra Ctrl-m at the beginning.
902 (and (eq selective-display t)
906 (setq startpos (point))))
907 (cons buf startpos))))
910 (defun find-tag-at-point (tagname &optional other-window)
911 "*Find tag whose name contains TAGNAME.
912 Identical to `find-tag' but does not prompt for tag when called interactively;
913 instead, uses tag around or before point."
914 (interactive (if current-prefix-arg
916 (list (find-tag-default) nil)))
917 (find-tag tagname other-window))
920 (defun find-tag (tagname &optional other-window)
921 "*Find tag whose name contains TAGNAME.
922 Selects the buffer that the tag is contained in
923 and puts point at its definition.
924 If TAGNAME is a null string, the expression in the buffer
925 around or before point is used as the tag name.
926 If called interactively with a numeric argument, searches for the next tag
927 in the tag table that matches the tagname used in the previous find-tag.
928 If second arg OTHER-WINDOW is non-nil, uses another window to display
931 This version of this function supports multiple active tags tables,
936 tag-table-alist controls which tables apply to which buffers
937 tags-file-name a default tags table
938 tags-build-completion-table controls completion behavior
939 buffer-tag-table another way of specifying a buffer-local table
940 make-tags-files-invisible whether tags tables should be very hidden
941 tag-mark-stack-max how many tags-based hops to remember"
942 (interactive (if current-prefix-arg
944 (list (find-tag-tag "Find tag: ") nil)))
945 (let* ((local-find-tag-hook find-tag-hook)
946 (next (null tagname))
947 (result (find-tag-internal tagname))
948 (tag-buf (car result))
949 (tag-point (cdr result)))
950 ;; Push old position on the tags mark stack.
952 (not (memq last-command
953 '(find-tag find-tag-other-window tags-loop-continue))))
956 (pop-to-buffer tag-buf t)
957 (switch-to-buffer tag-buf))
960 (goto-char tag-point)
962 (run-hooks 'find-tag-hook)
963 (if local-find-tag-hook
964 (run-hooks 'local-find-tag-hook))))
965 (setq tags-loop-scan (list 'find-tag nil nil)
966 tags-loop-operate nil)
967 ;; Return t in case used as the tags-loop-scan.
971 (defun find-tag-other-window (tagname &optional next)
972 "*Find tag whose name contains TAGNAME, in another window.
973 Selects the buffer that the tag is contained in in another window
974 and puts point at its definition.
975 If TAGNAME is a null string, the expression in the buffer
976 around or before point is used as the tag name.
977 If second arg NEXT is non-nil (interactively, with prefix arg),
978 searches for the next tag in the tag table
979 that matches the tagname used in the previous find-tag.
981 This version of this function supports multiple active tags tables,
986 tag-table-alist controls which tables apply to which buffers
987 tags-file-name a default tags table
988 tags-build-completion-table controls completion behavior
989 buffer-tag-table another way of specifying a buffer-local table
990 make-tags-files-invisible whether tags tables should be very hidden
991 tag-mark-stack-max how many tags-based hops to remember"
992 (interactive (if current-prefix-arg
994 (list (find-tag-tag "Find tag other window: "))))
997 (find-tag tagname t)))
1000 ;; Completion on tags in the buffer.
1002 (defun complete-symbol (&optional table predicate prettify)
1003 (let* ((end (point))
1004 (beg (save-excursion
1006 ;;(while (= (char-syntax (following-char)) ?\')
1007 ;; (forward-char 1))
1008 (skip-syntax-forward "'")
1010 (pattern (buffer-substring beg end))
1011 (table (or table obarray))
1012 (completion (try-completion pattern table predicate)))
1013 (cond ((eq completion t))
1015 (error "Can't find completion for \"%s\"" pattern))
1016 ((not (string-equal pattern completion))
1017 (delete-region beg end)
1018 (insert completion))
1020 (message "Making completion list...")
1021 (let ((list (all-completions pattern table predicate)))
1023 (setq list (funcall prettify list)))
1024 (with-output-to-temp-buffer "*Help*"
1025 (display-completion-list list)))
1026 (message "Making completion list...%s" "done")))))
1029 (defun tag-complete-symbol ()
1030 "The function used to do tags-completion (using 'tag-completion-predicate)."
1032 (let* ((buffer-tag-table-list (buffer-tag-table-symbol-list))
1034 (complete-symbol tag-completion-table 'tag-completion-predicate)))
1037 ;; Applying a command to files mentioned in tag tables
1039 (defvar next-file-list nil
1040 "List of files for next-file to process.")
1043 (defun next-file (&optional initialize novisit)
1044 "Select next file among files in current tag table(s).
1046 A first argument of t (prefix arg, if interactive) initializes to the
1047 beginning of the list of files in the (first) tags table. If the argument
1048 is neither nil nor t, it is evalled to initialize the list of files.
1050 Non-nil second argument NOVISIT means use a temporary buffer
1051 to save time and avoid uninteresting warnings.
1053 Value is nil if the file was already visited;
1054 if the file was newly read in, the value is the filename."
1056 (cond ((not initialize)
1057 ;; Not the first run.
1060 ;; Initialize the list from the tags table.
1061 (setq next-file-list (buffer-tag-table-files)))
1063 ;; Initialize the list by evalling the argument.
1064 (setq next-file-list (eval initialize))))
1065 (when (null next-file-list)
1067 (get-buffer " *next-file*")
1068 (kill-buffer " *next-file*"))
1069 (error "All files processed"))
1070 (let* ((file (car next-file-list))
1071 (buf (get-file-buffer file))
1073 (pop next-file-list)
1075 (if (not (and new novisit))
1076 (switch-to-buffer (find-file-noselect file novisit) t)
1077 ;; Like find-file, but avoids random junk.
1078 (set-buffer (get-buffer-create " *next-file*"))
1079 (kill-all-local-variables)
1081 (insert-file-contents file nil))
1083 (when (> (point) (point-min))
1085 (goto-char (point-min)))
1089 (defun tags-loop-continue (&optional first-time)
1090 "Continue last \\[tags-search] or \\[tags-query-replace] command.
1091 Used noninteractively with non-nil argument to begin such a command (the
1092 argument is passed to `next-file', which see).
1093 Two variables control the processing we do on each file:
1094 the value of `tags-loop-scan' is a form to be executed on each file
1095 to see if it is interesting (it returns non-nil if so)
1096 and `tags-loop-operate' is a form to execute to operate on an interesting file
1097 If the latter returns non-nil, we exit; otherwise we scan the next file."
1099 (let ((messaged nil)
1103 ;; Scan files quickly for the first or next interesting one.
1104 (while (or first-time
1107 (not (eval tags-loop-scan))))
1108 (setq new (next-file first-time
1109 tags-search-nuke-uninteresting-buffers))
1110 ;; If NEW is non-nil, we got a temp buffer,
1111 ;; and NEW is the file name.
1113 (and (not first-time)
1114 (> (device-baud-rate) search-slow-speed)
1117 "Scanning file %s..." (or new buffer-file-name)))
1118 (setq first-time nil)
1119 (goto-char (point-min)))
1121 ;; If we visited it in a temp buffer, visit it now for real.
1122 (if (and new tags-search-nuke-uninteresting-buffers)
1123 (let ((pos (point)))
1125 (set-buffer (find-file-noselect new))
1129 (switch-to-buffer (current-buffer))
1131 ;; Now operate on the file.
1132 ;; If value is non-nil, continue to scan the next file.
1133 (setq more-files-p (eval tags-loop-operate)))
1135 (null tags-loop-operate)
1136 (message "Scanning file %s...found" buffer-file-name))))
1140 (defun tags-search (regexp &optional file-list-form)
1141 "Search through all files listed in tags table for match for REGEXP.
1142 Stops when a match is found.
1143 To continue searching for next match, use command \\[tags-loop-continue].
1145 See documentation of variable `tag-table-alist'."
1146 (interactive "sTags search (regexp): ")
1147 (if (and (equal regexp "")
1148 (eq (car tags-loop-scan) 'with-search-caps-disable-folding)
1149 (null tags-loop-operate))
1150 ;; Continue last tags-search as if by `M-,'.
1151 (tags-loop-continue nil)
1152 (setq tags-loop-scan `(with-search-caps-disable-folding ,regexp t
1153 (re-search-forward ,regexp nil t))
1154 tags-loop-operate nil)
1155 (tags-loop-continue (or file-list-form t))))
1158 (defun tags-query-replace (from to &optional delimited file-list-form)
1159 "Query-replace-regexp FROM with TO through all files listed in tags table.
1160 Third arg DELIMITED (prefix arg) means replace only word-delimited matches.
1161 If you exit (\\[keyboard-quit] or ESC), you can resume the query-replace
1162 with the command \\[tags-loop-continue].
1164 See documentation of variable `tag-table-alist'."
1166 "sTags query replace (regexp): \nsTags query replace %s by: \nP")
1167 (setq tags-loop-scan `(with-search-caps-disable-folding ,from t
1168 (if (re-search-forward ,from nil t)
1169 ;; When we find a match, move back
1170 ;; to the beginning of it so perform-replace
1172 (progn (goto-char (match-beginning 0)) t)))
1173 tags-loop-operate (list 'perform-replace from to t t
1174 (not (null delimited))))
1175 (tags-loop-continue (or file-list-form t)))
1180 (defun list-tags (file)
1181 "Display list of tags in FILE."
1182 (interactive (list (read-file-name
1183 (if (buffer-file-name)
1184 (format "List tags (in file, %s by default): "
1185 (file-name-nondirectory (buffer-file-name)))
1186 "List tags (in file): ")
1187 nil (buffer-file-name) t)))
1188 (find-file-noselect file)
1189 (with-output-to-temp-buffer "*Tags List*"
1190 (princ "Tags in file ")
1194 (dolist (tags-file (with-current-buffer (get-file-buffer file)
1195 (buffer-tag-table-list)))
1196 ;; We don't want completions getting in the way.
1197 (let ((tags-build-completion-table nil))
1198 (set-buffer (get-tag-table-buffer tags-file)))
1199 (goto-char (point-min))
1201 (search-forward (concat "\f\n" (file-name-nondirectory file) ",")
1204 (while (not (or (eobp) (looking-at "\f")))
1205 (princ (buffer-substring (point)
1206 (progn (skip-chars-forward "^\C-?")
1209 (forward-line 1)))))))
1212 (defun tags-apropos (string)
1213 "Display list of all tags in tag table REGEXP matches."
1214 (interactive "sTag apropos (regexp): ")
1215 (with-output-to-temp-buffer "*Tags List*"
1216 (princ "Tags matching regexp ")
1220 (visit-tags-table-buffer)
1222 (while (re-search-forward string nil t)
1224 (princ (buffer-substring (point)
1225 (progn (skip-chars-forward "^\C-?")
1228 (forward-line 1)))))
1230 ;; #### copied from tags.el. This function is *very* big in FSF.
1231 (defun visit-tags-table-buffer ()
1232 "Select the buffer containing the current tag table."
1234 (call-interactively 'visit-tags-table))
1235 (set-buffer (or (get-file-buffer tags-file-name)
1237 (setq tag-table-files nil)
1238 (find-file-noselect tags-file-name))))
1239 (or (verify-visited-file-modtime (get-file-buffer tags-file-name))
1240 (cond ((yes-or-no-p "Tags file has changed, read new contents? ")
1242 (setq tag-table-files nil))))
1243 (or (eq (char-after 1) ?\^L)
1244 (error "File %s not a valid tag table" tags-file-name)))
1247 ;; Sample uses of find-tag-hook and find-tag-default-hook
1249 ;; This is wrong. We should either make this behavior default and
1250 ;; back it up, or not use it at all. For now, I've commented it out.
1253 ;; Example buffer-local tag finding
1255 ;(add-hook 'emacs-lisp-mode-hook 'setup-emacs-lisp-default-tag-hook)
1257 ;(defun setup-emacs-lisp-default-tag-hook ()
1258 ; (cond ((eq major-mode 'emacs-lisp-mode)
1259 ; (make-variable-buffer-local 'find-tag-default-hook)
1260 ; (setq find-tag-default-hook 'emacs-lisp-default-tag))))
1261 ;;; Run it once immediately
1262 ;(setup-emacs-lisp-default-tag-hook)
1263 ;(when (get-buffer "*scratch*")
1264 ; (with-current-buffer "*scratch*"
1265 ; (setup-emacs-lisp-default-tag-hook)))
1267 ;(defun emacs-lisp-default-tag ()
1268 ; "Function to return a default tag for Emacs-Lisp mode."
1269 ; (let ((tag (or (variable-at-point)
1270 ; (function-at-point))))
1271 ; (if tag (symbol-name tag))))
1274 ;; Display short info on tag in minibuffer
1276 ;; Don't pollute `M-?' -- we may need it for more important stuff. --hniksic
1277 ;(if (null (lookup-key esc-map "?"))
1278 ; (define-key esc-map "?" 'display-tag-info))
1280 (defun display-tag-info (tagname)
1281 "Prints a description of the first tag matching TAGNAME in the echo area.
1282 If this is an elisp function, prints something like \"(defun foo (x y z)\".
1283 That is, is prints the first line of the definition of the form.
1284 If this is a C-defined elisp function, it does something more clever."
1285 (interactive (if current-prefix-arg
1287 (list (find-tag-tag "Display tag info: "))))
1288 (let* ((results (find-tag-internal tagname))
1289 (tag-buf (car results))
1290 (tag-point (cdr results))
1291 info lname min max fname args)
1292 (with-current-buffer tag-buf
1296 (goto-char tag-point)
1297 (cond ((let ((case-fold-search nil))
1298 (looking-at "^DEFUN[ \t]"))
1301 (setq lname (read (current-buffer))
1302 fname (buffer-substring
1303 (progn (forward-sexp 1) (point))
1304 (progn (backward-sexp 1) (point)))
1305 min (buffer-substring
1306 (progn (forward-sexp 3) (point))
1307 (progn (backward-sexp 1) (point)))
1308 max (buffer-substring
1309 (progn (forward-sexp 2) (point))
1310 (progn (backward-sexp 1) (point))))
1311 (backward-up-list 1)
1312 (setq args (buffer-substring
1313 (progn (forward-sexp 2) (point))
1314 (progn (backward-sexp 1) (point))))
1315 (setq info (format "Elisp: %s, C: %s %s, #args: %s"
1318 (if (string-equal min max)
1320 (format "from %s to %s" min max)))))
1324 (progn (beginning-of-line) (point))
1325 (progn (end-of-line) (point)))))))))
1326 (message "%s" info))
1327 (setq tags-loop-scan '(display-tag-info nil)
1328 tags-loop-operate nil)
1329 ;; Always return non-nil
1335 (defvar tag-mark-stack1 nil)
1336 (defvar tag-mark-stack2 nil)
1338 (defcustom tag-mark-stack-max 16
1339 "*The maximum number of elements kept on the mark-stack used
1340 by tags-search. See also the commands `\\[push-tag-mark]' and
1341 and `\\[pop-tag-mark]'."
1345 (defun push-mark-on-stack (stack-symbol &optional max-size)
1346 (let ((stack (symbol-value stack-symbol)))
1347 (push (point-marker) stack)
1348 (cond ((and max-size
1349 (> (length stack) max-size))
1350 (set-marker (car (nthcdr max-size stack)) nil)
1351 (setcdr (nthcdr (1- max-size) stack) nil)))
1352 (set stack-symbol stack)))
1354 (defun pop-mark-from-stack (stack-symbol1 stack-symbol2 &optional max-size)
1355 (let* ((stack (or (symbol-value stack-symbol1)
1356 (error "No more tag marks on stack")))
1357 (marker (car stack))
1358 (m-buf (marker-buffer marker)))
1359 (set stack-symbol1 (cdr stack))
1361 (error "Marker has no buffer"))
1362 (or (buffer-live-p m-buf)
1363 (error "Buffer has been killed"))
1364 (push-mark-on-stack stack-symbol2 max-size)
1365 (switch-to-buffer m-buf)
1367 (goto-char marker)))
1369 (defun push-tag-mark ()
1370 (push-mark-on-stack 'tag-mark-stack1 tag-mark-stack-max))
1372 ;;;###autoload (define-key esc-map "*" 'pop-tag-mark)
1375 (defun pop-tag-mark (arg)
1376 "Go to last tag position.
1377 `find-tag' maintains a mark-stack seperate from the \\[set-mark-command] mark-stack.
1378 This function pops (and moves to) the tag at the top of this stack."
1381 (pop-mark-from-stack
1382 'tag-mark-stack1 'tag-mark-stack2 tag-mark-stack-max)
1383 (pop-mark-from-stack
1384 'tag-mark-stack2 'tag-mark-stack1 tag-mark-stack-max)))
1390 ;;; etags.el ends here