* progmodes/etags.el (next-file): Improve revert message.
[bpt/emacs.git] / lisp / progmodes / etags.el
CommitLineData
c8472948 1;;; etags.el --- etags facility for Emacs
8c8f9bc1 2
034babe1 3;; Copyright (C) 1985, 1986, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1998,
4e643dd2 4;; 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
bf349f36 5;; Free Software Foundation, Inc.
ff1f0fa6 6
5762abec 7;; Author: Roland McGrath <roland@gnu.org>
d6b8c85b 8;; Maintainer: FSF
3a801d0c
ER
9;; Keywords: tools
10
ff1f0fa6
JB
11;; This file is part of GNU Emacs.
12
13;; GNU Emacs is free software; you can redistribute it and/or modify
14;; it under the terms of the GNU General Public License as published by
1a484753 15;; the Free Software Foundation; either version 3, or (at your option)
ff1f0fa6
JB
16;; any later version.
17
18;; GNU Emacs is distributed in the hope that it will be useful,
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
22
23;; You should have received a copy of the GNU General Public License
b578f267 24;; along with GNU Emacs; see the file COPYING. If not, write to the
3a35cf56
LK
25;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26;; Boston, MA 02110-1301, USA.
ff1f0fa6 27
6010664f
SM
28;;; Commentary:
29
e5167999
ER
30;;; Code:
31
b533f3c5 32(require 'ring)
89228b63 33(require 'button)
b533f3c5 34
c086701a 35;;;###autoload
b6176f64
RM
36(defvar tags-file-name nil
37 "*File name of tags table.
9708f7fc 38To switch to a new tags table, setting this variable is sufficient.
b6176f64 39If you set this variable, do not also set `tags-table-list'.
9708f7fc 40Use the `etags' program to make a tags table file.")
b6176f64 41;; Make M-x set-variable tags-file-name like M-x visit-tags-table.
9ef8b0d6 42;;;###autoload (put 'tags-file-name 'variable-interactive "fVisit tags table: ")
9708f7fc 43
99730b0e 44(defgroup etags nil "Tags tables."
99783bde
RS
45 :group 'tools)
46
ece6e35a
GM
47;;;###autoload
48(defcustom tags-case-fold-search 'default
49 "*Whether tags operations should be case-sensitive.
50A value of t means case-insensitive, a value of nil means case-sensitive.
51Any other value means use the setting of `case-fold-search'."
52 :group 'etags
53 :type '(choice (const :tag "Case-sensitive" nil)
54 (const :tag "Case-insensitive" t)
55 (other :tag "Use default" default))
56 :version "21.1")
57
9708f7fc 58;;;###autoload
b6176f64 59;; Use `visit-tags-table-buffer' to cycle through tags tables in this list.
99783bde 60(defcustom tags-table-list nil
b6176f64
RM
61 "*List of file names of tags tables to search.
62An element that is a directory means the file \"TAGS\" in that directory.
63To switch to a new list of tags tables, setting this variable is sufficient.
64If you set this variable, do not also set `tags-file-name'.
99783bde
RS
65Use the `etags' program to make a tags table file."
66 :group 'etags
67 :type '(repeat file))
9708f7fc 68
ddc76b00
FP
69;;;###autoload
70(defcustom tags-compression-info-list '("" ".Z" ".bz2" ".gz" ".tgz")
71 "*List of extensions tried by etags when jka-compr is used.
72An empty string means search the non-compressed file.
73These extensions will be tried only if jka-compr was activated
1ea98518
SM
74\(i.e. via customize of `auto-compression-mode' or by calling the function
75`auto-compression-mode')."
76 :type '(repeat string)
ddc76b00
FP
77 :group 'etags)
78
1ea98518
SM
79;; !!! tags-compression-info-list should probably be replaced by access
80;; to directory list and matching jka-compr-compression-info-list. Currently,
81;; this implementation forces each modification of
82;; jka-compr-compression-info-list to be reflected in this var.
83;; An alternative could be to say that introducing a special
84;; element in this list (e.g. t) means : try at this point
85;; using directory listing and regexp matching using
86;; jka-compr-compression-info-list.
ddc76b00
FP
87
88
33633b28 89;;;###autoload
99783bde 90(defcustom tags-add-tables 'ask-user
0c37b824
RS
91 "*Control whether to add a new tags table to the current list.
92t means do; nil means don't (always start a new list).
93Any other value means ask the user whether to add a new tags table
99783bde
RS
94to the current list (as opposed to starting a new list)."
95 :group 'etags
96 :type '(choice (const :tag "Do" t)
97 (const :tag "Don't" nil)
add3e8b3 98 (other :tag "Ask" ask-user)))
99783bde
RS
99
100(defcustom tags-revert-without-query nil
101 "*Non-nil means reread a TAGS table without querying, if it has changed."
102 :group 'etags
103 :type 'boolean)
5f8cdaf2 104
a3d358c6
RM
105(defvar tags-table-computed-list nil
106 "List of tags tables to search, computed from `tags-table-list'.
107This includes tables implicitly included by other tables. The list is not
108always complete: the included tables of a table are not known until that
6010664f 109table is read into core. An element that is t is a placeholder
a3d358c6
RM
110indicating that the preceding element is a table that has not been read
111into core and might contain included tables to search.
112See `tags-table-check-computed-list'.")
113
114(defvar tags-table-computed-list-for nil
115 "Value of `tags-table-list' that `tags-table-computed-list' corresponds to.
116If `tags-table-list' changes, `tags-table-computed-list' is thrown away and
117recomputed; see `tags-table-check-computed-list'.")
118
9708f7fc 119(defvar tags-table-list-pointer nil
a3d358c6 120 "Pointer into `tags-table-computed-list' for the current state of searching.
47f3c459
RM
121Use `visit-tags-table-buffer' to cycle through tags tables in this list.")
122
123(defvar tags-table-list-started-at nil
a3d358c6 124 "Pointer into `tags-table-computed-list', where the current search started.")
9708f7fc
RM
125
126(defvar tags-table-set-list nil
127 "List of sets of tags table which have been used together in the past.
128Each element is a list of strings which are file names.")
129
130;;;###autoload
99783bde 131(defcustom find-tag-hook nil
9708f7fc
RM
132 "*Hook to be run by \\[find-tag] after finding a tag. See `run-hooks'.
133The value in the buffer in which \\[find-tag] is done is used,
99783bde
RS
134not the value in the buffer \\[find-tag] goes to."
135 :group 'etags
136 :type 'hook)
9708f7fc
RM
137
138;;;###autoload
99783bde 139(defcustom find-tag-default-function nil
47f3c459
RM
140 "*A function of no arguments used by \\[find-tag] to pick a default tag.
141If nil, and the symbol that is the value of `major-mode'
9708f7fc 142has a `find-tag-default-function' property (see `put'), that is used.
99783bde
RS
143Otherwise, `find-tag-default' is used."
144 :group 'etags
d639db36 145 :type '(choice (const nil) function))
9708f7fc 146
b533f3c5
RS
147(defcustom find-tag-marker-ring-length 16
148 "*Length of marker rings `find-tag-marker-ring' and `tags-location-ring'."
149 :group 'etags
cd32a7ba
DN
150 :type 'integer
151 :version "20.3")
b533f3c5 152
7e7b42b2
GM
153(defcustom tags-tag-face 'default
154 "*Face for tags in the output of `tags-apropos'."
155 :group 'etags
156 :type 'face
157 :version "21.1")
158
159(defcustom tags-apropos-verbose nil
160 "If non-nil, print the name of the tags file in the *Tags List* buffer."
161 :group 'etags
162 :type 'boolean
163 :version "21.1")
164
165(defcustom tags-apropos-additional-actions nil
166 "Specify additional actions for `tags-apropos'.
167
168If non-nil, value should be a list of triples (TITLE FUNCTION
169TO-SEARCH). For each triple, `tags-apropos' processes TO-SEARCH and
170lists tags from it. TO-SEARCH should be an alist, obarray, or symbol.
171If it is a symbol, the symbol's value is used.
6010664f 172TITLE, a string, is a title used to label the additional list of tags.
7e7b42b2
GM
173FUNCTION is a function to call when a symbol is selected in the
174*Tags List* buffer. It will be called with one argument SYMBOL which
175is the symbol being selected.
176
177Example value:
178
179 '((\"Emacs Lisp\" Info-goto-emacs-command-node obarray)
180 (\"Common Lisp\" common-lisp-hyperspec common-lisp-hyperspec-obarray)
181 (\"SCWM\" scwm-documentation scwm-obarray))"
182 :group 'etags
61520f26
DL
183 :type '(repeat (list (string :tag "Title")
184 function
185 (sexp :tag "Tags to search")))
7e7b42b2
GM
186 :version "21.1")
187
b533f3c5
RS
188(defvar find-tag-marker-ring (make-ring find-tag-marker-ring-length)
189 "Ring of markers which are locations from which \\[find-tag] was invoked.")
190
9708f7fc 191(defvar default-tags-table-function nil
5f8cdaf2
RS
192 "If non-nil, a function to choose a default tags file for a buffer.
193This function receives no arguments and should return the default
194tags table file to use for the current buffer.")
d74e816f 195
b533f3c5
RS
196(defvar tags-location-ring (make-ring find-tag-marker-ring-length)
197 "Ring of markers which are locations visited by \\[find-tag].
d74e816f 198Pop back to the last location with \\[negative-argument] \\[find-tag].")
9708f7fc
RM
199\f
200;; Tags table state.
201;; These variables are local in tags table buffers.
ff1f0fa6 202
9708f7fc
RM
203(defvar tags-table-files nil
204 "List of file names covered by current tags table.
205nil means it has not yet been computed; use `tags-table-files' to do so.")
206
207(defvar tags-completion-table nil
7e7b42b2 208 "Obarray of tag names defined in current tags table.")
9708f7fc
RM
209
210(defvar tags-included-tables nil
211 "List of tags tables included by the current tags table.")
212
213(defvar next-file-list nil
214 "List of files for \\[next-file] to process.")
215\f
216;; Hooks for file formats.
217
d6b8c85b
SM
218(defvar tags-table-format-functions '(etags-recognize-tags-table
219 tags-recognize-empty-tags-table)
6010664f 220 "Hook to be called in a tags table buffer to identify the type of tags table.
b533f3c5 221The functions are called in order, with no arguments,
9708f7fc
RM
222until one returns non-nil. The function should make buffer-local bindings
223of the format-parsing tags function variables if successful.")
224
225(defvar file-of-tag-function nil
79e01623
JB
226 "Function to do the work of `file-of-tag' (which see).
227One optional argument, a boolean specifying to return complete path (nil) or
228relative path (non-nil).")
9708f7fc
RM
229(defvar tags-table-files-function nil
230 "Function to do the work of `tags-table-files' (which see).")
231(defvar tags-completion-table-function nil
6010664f 232 "Function to build the `tags-completion-table'.")
9708f7fc 233(defvar snarf-tag-function nil
79e01623
JB
234 "Function to get info about a matched tag for `goto-tag-location-function'.
235One optional argument, specifying to use explicit tag (non-nil) or not (nil).
236The default is nil.")
9708f7fc
RM
237(defvar goto-tag-location-function nil
238 "Function of to go to the location in the buffer specified by a tag.
239One argument, the tag info returned by `snarf-tag-function'.")
240(defvar find-tag-regexp-search-function nil
241 "Search function passed to `find-tag-in-order' for finding a regexp tag.")
242(defvar find-tag-regexp-tag-order nil
243 "Tag order passed to `find-tag-in-order' for finding a regexp tag.")
244(defvar find-tag-regexp-next-line-after-failure-p nil
245 "Flag passed to `find-tag-in-order' for finding a regexp tag.")
246(defvar find-tag-search-function nil
247 "Search function passed to `find-tag-in-order' for finding a tag.")
248(defvar find-tag-tag-order nil
249 "Tag order passed to `find-tag-in-order' for finding a tag.")
250(defvar find-tag-next-line-after-failure-p nil
251 "Flag passed to `find-tag-in-order' for finding a tag.")
252(defvar list-tags-function nil
253 "Function to do the work of `list-tags' (which see).")
254(defvar tags-apropos-function nil
255 "Function to do the work of `tags-apropos' (which see).")
256(defvar tags-included-tables-function nil
257 "Function to do the work of `tags-included-tables' (which see).")
258(defvar verify-tags-table-function nil
e7f767c2 259 "Function to return t if current buffer contains valid tags file.")
9708f7fc 260\f
b6176f64 261;; Initialize the tags table in the current buffer.
e7f767c2 262;; Returns non-nil if it is a valid tags table. On
b6176f64
RM
263;; non-nil return, the tags table state variable are
264;; made buffer-local and initialized to nil.
9708f7fc 265(defun initialize-new-tags-table ()
b6176f64
RM
266 (set (make-local-variable 'tags-table-files) nil)
267 (set (make-local-variable 'tags-completion-table) nil)
268 (set (make-local-variable 'tags-included-tables) nil)
931f525c
RS
269 ;; We used to initialize find-tag-marker-ring and tags-location-ring
270 ;; here, to new empty rings. But that is wrong, because those
271 ;; are global.
272
9708f7fc 273 ;; Value is t if we have found a valid tags table buffer.
d6b8c85b 274 (run-hook-with-args-until-success 'tags-table-format-functions))
ff1f0fa6 275
0f518b6b
JL
276;;;###autoload
277(defun tags-table-mode ()
278 "Major mode for tags table file buffers."
279 (interactive)
5ec19a11
GM
280 (setq major-mode 'tags-table-mode
281 mode-name "Tags Table"
8e004a44 282 buffer-undo-list t)
0f518b6b
JL
283 (initialize-new-tags-table))
284
c086701a 285;;;###autoload
9708f7fc
RM
286(defun visit-tags-table (file &optional local)
287 "Tell tags commands to use tags table file FILE.
ff1f0fa6 288FILE should be the name of a file created with the `etags' program.
9708f7fc
RM
289A directory name is ok too; it means file TAGS in that directory.
290
291Normally \\[visit-tags-table] sets the global value of `tags-file-name'.
292With a prefix arg, set the buffer-local value instead.
293When you find a tag with \\[find-tag], the buffer it finds the tag
294in is given a local value of this variable which is the name of the tags
295file the tag was in."
5b76833f 296 (interactive (list (read-file-name "Visit tags table (default TAGS): "
ff1f0fa6 297 default-directory
9708f7fc
RM
298 (expand-file-name "TAGS"
299 default-directory)
300 t)
301 current-prefix-arg))
aab936ad
RM
302 (or (stringp file) (signal 'wrong-type-argument (list 'stringp file)))
303 ;; Bind tags-file-name so we can control below whether the local or
304 ;; global value gets set. Calling visit-tags-table-buffer will
305 ;; initialize a buffer for the file and set tags-file-name to the
5e882a6a
RS
306 ;; Calling visit-tags-table-buffer with tags-file-name set to FILE will
307 ;; initialize a buffer for FILE and set tags-file-name to the
b6176f64 308 ;; fully-expanded name.
9806213d
RM
309 (let ((tags-file-name file))
310 (save-excursion
aab936ad 311 (or (visit-tags-table-buffer file)
9806213d
RM
312 (signal 'file-error (list "Visiting tags table"
313 "file does not exist"
314 file)))
b6176f64 315 ;; Set FILE to the expanded name.
9806213d 316 (setq file tags-file-name)))
9708f7fc 317 (if local
b6176f64 318 ;; Set the local value of tags-file-name.
47f3c459 319 (set (make-local-variable 'tags-file-name) file)
b6176f64 320 ;; Set the global value of tags-file-name.
9806213d
RM
321 (setq-default tags-file-name file)))
322
a3d358c6
RM
323(defun tags-table-check-computed-list ()
324 "Compute `tags-table-computed-list' from `tags-table-list' if necessary."
325 (let ((expanded-list (mapcar 'tags-expand-table-name tags-table-list)))
326 (or (equal tags-table-computed-list-for expanded-list)
327 ;; The list (or default-directory) has changed since last computed.
328 (let* ((compute-for (mapcar 'copy-sequence expanded-list))
329 (tables (copy-sequence compute-for)) ;Mutated in the loop.
330 (computed nil)
331 table-buffer)
332
333 (while tables
334 (setq computed (cons (car tables) computed)
335 table-buffer (get-file-buffer (car tables)))
83287e5b 336 (if (and table-buffer
c49a777a
RM
337 ;; There is a buffer visiting the file. Now make sure
338 ;; it is initialized as a tag table buffer.
339 (save-excursion
340 (tags-verify-table (buffer-file-name table-buffer))))
a3d358c6
RM
341 (save-excursion
342 (set-buffer table-buffer)
343 (if (tags-included-tables)
344 ;; Insert the included tables into the list we
345 ;; are processing.
b1c7e434
RM
346 (setcdr tables (nconc (mapcar 'tags-expand-table-name
347 (tags-included-tables))
348 (cdr tables)))))
a3d358c6
RM
349 ;; This table is not in core yet. Insert a placeholder
350 ;; saying we must read it into core to check for included
351 ;; tables before searching the next table in the list.
352 (setq computed (cons t computed)))
353 (setq tables (cdr tables)))
354
355 ;; Record the tags-table-list value (and the context of the
356 ;; current directory) we computed from.
357 (setq tags-table-computed-list-for compute-for
358 tags-table-computed-list (nreverse computed))))))
359
360;; Extend `tags-table-computed-list' to remove the first `t' placeholder.
361;; An element of the list that is `t' is a placeholder indicating that the
362;; preceding element is a table that has not been read into core and might
363;; contain included tables to search. On return, the first placeholder
364;; element will be gone and the element before it read into core and its
365;; included tables inserted into the list.
366(defun tags-table-extend-computed-list ()
367 (let ((list tags-table-computed-list))
368 (while (not (eq (nth 1 list) t))
369 (setq list (cdr list)))
370 (save-excursion
371 (if (tags-verify-table (car list))
372 ;; We are now in the buffer visiting (car LIST). Extract its
373 ;; list of included tables and insert it into the computed list.
374 (let ((tables (tags-included-tables))
375 (computed nil)
376 table-buffer)
377 (while tables
378 (setq computed (cons (car tables) computed)
379 table-buffer (get-file-buffer (car tables)))
380 (if table-buffer
381 (save-excursion
382 (set-buffer table-buffer)
383 (if (tags-included-tables)
384 ;; Insert the included tables into the list we
385 ;; are processing.
386 (setcdr tables (append (tags-included-tables)
387 tables))))
388 ;; This table is not in core yet. Insert a placeholder
389 ;; saying we must read it into core to check for included
390 ;; tables before searching the next table in the list.
e99045bb
RM
391 (setq computed (cons t computed)))
392 (setq tables (cdr tables)))
a3d358c6
RM
393 (setq computed (nreverse computed))
394 ;; COMPUTED now contains the list of included tables (and
395 ;; tables included by them, etc.). Now splice this into the
396 ;; current list.
397 (setcdr list (nconc computed (cdr (cdr list)))))
398 ;; It was not a valid table, so just remove the following placeholder.
399 (setcdr list (cdr (cdr list)))))))
b6176f64
RM
400
401;; Expand tags table name FILE into a complete file name.
47f3c459 402(defun tags-expand-table-name (file)
47f3c459
RM
403 (setq file (expand-file-name file))
404 (if (file-directory-p file)
405 (expand-file-name "TAGS" file)
406 file))
407
a3d358c6
RM
408;; Like member, but comparison is done after tags-expand-table-name on both
409;; sides and elements of LIST that are t are skipped.
410(defun tags-table-list-member (file list)
47f3c459 411 (setq file (tags-expand-table-name file))
a3d358c6
RM
412 (while (and list
413 (or (eq (car list) t)
414 (not (string= file (tags-expand-table-name (car list))))))
415 (setq list (cdr list)))
47f3c459
RM
416 list)
417
a3d358c6
RM
418(defun tags-verify-table (file)
419 "Read FILE into a buffer and verify that it is a valid tags table.
420Sets the current buffer to one visiting FILE (if it exists).
e7f767c2 421Returns non-nil if it is a valid table."
a3d358c6
RM
422 (if (get-file-buffer file)
423 ;; The file is already in a buffer. Check for the visited file
424 ;; having changed since we last used it.
425 (let (win)
426 (set-buffer (get-file-buffer file))
0f518b6b 427 (setq win (or verify-tags-table-function (tags-table-mode)))
a3d358c6 428 (if (or (verify-visited-file-modtime (current-buffer))
350ce4cf
RS
429 ;; Decide whether to revert the file.
430 ;; revert-without-query can say to revert
431 ;; or the user can say to revert.
432 (not (or (let ((tail revert-without-query)
433 (found nil))
434 (while tail
435 (if (string-match (car tail) buffer-file-name)
436 (setq found t))
437 (setq tail (cdr tail)))
438 found)
99783bde 439 tags-revert-without-query
350ce4cf
RS
440 (yes-or-no-p
441 (format "Tags file %s has changed, read new contents? "
442 file)))))
443 (and verify-tags-table-function
444 (funcall verify-tags-table-function))
a3d358c6 445 (revert-buffer t t)
0f518b6b 446 (tags-table-mode)))
a3d358c6
RM
447 (and (file-exists-p file)
448 (progn
449 (set-buffer (find-file-noselect file))
450 (or (string= file buffer-file-name)
451 ;; find-file-noselect has changed the file name.
452 ;; Propagate the change to tags-file-name and tags-table-list.
453 (let ((tail (member file tags-table-list)))
454 (if tail
455 (setcar tail buffer-file-name))
456 (if (eq file tags-file-name)
457 (setq tags-file-name buffer-file-name))))
0f518b6b 458 (tags-table-mode)))))
a3d358c6
RM
459
460;; Subroutine of visit-tags-table-buffer. Search the current tags tables
461;; for one that has tags for THIS-FILE (or that includes a table that
c3dea9ba
RM
462;; does). Return the name of the first table table listing THIS-FILE; if
463;; the table is one included by another table, it is the master table that
464;; we return. If CORE-ONLY is non-nil, check only tags tables that are
465;; already in buffers--don't visit any new files.
a3d358c6 466(defun tags-table-including (this-file core-only)
a3d358c6 467 (let ((tables tags-table-computed-list)
f06df563 468 (found nil))
a3d358c6 469 ;; Loop over the list, looking for a table containing tags for THIS-FILE.
47f3c459
RM
470 (while (and (not found)
471 tables)
9d591df8
RM
472
473 (if core-only
474 ;; Skip tables not in core.
475 (while (eq (nth 1 tables) t)
476 (setq tables (cdr (cdr tables))))
477 (if (eq (nth 1 tables) t)
478 ;; This table has not been read into core yet. Read it in now.
479 (tags-table-extend-computed-list)))
a3d358c6 480
3cc3f703
RM
481 (if tables
482 ;; Select the tags table buffer and get the file list up to date.
483 (let ((tags-file-name (car tables)))
484 (visit-tags-table-buffer 'same)
83287e5b
RM
485 (if (member this-file (mapcar 'expand-file-name
486 (tags-table-files)))
3cc3f703
RM
487 ;; Found it.
488 (setq found tables))))
47f3c459 489 (setq tables (cdr tables)))
3cc3f703
RM
490 (if found
491 ;; Now determine if the table we found was one included by another
82f75cca
RM
492 ;; table, not explicitly listed. We do this by checking each
493 ;; element of the computed list to see if it appears in the user's
494 ;; explicit list; the last element we will check is FOUND itself.
495 ;; Then we return the last one which did in fact appear in
496 ;; tags-table-list.
3cc3f703
RM
497 (let ((could-be nil)
498 (elt tags-table-computed-list))
499 (while (not (eq elt (cdr found)))
500 (if (tags-table-list-member (car elt) tags-table-list)
501 ;; This table appears in the user's list, so it could be
502 ;; the one which includes the table we found.
82f75cca
RM
503 (setq could-be (car elt)))
504 (setq elt (cdr elt))
505 (if (eq t (car elt))
506 (setq elt (cdr elt))))
ec623f1b
RM
507 ;; The last element we found in the computed list before FOUND
508 ;; that appears in the user's list will be the table that
83287e5b 509 ;; included the one we found.
82f75cca 510 could-be))))
9708f7fc 511
a3d358c6
RM
512;; Subroutine of visit-tags-table-buffer. Move tags-table-list-pointer
513;; along and set tags-file-name. Returns nil when out of tables.
514(defun tags-next-table ()
515 ;; If there is a placeholder element next, compute the list to replace it.
516 (while (eq (nth 1 tags-table-list-pointer) t)
517 (tags-table-extend-computed-list))
518
519 ;; Go to the next table in the list.
520 (setq tags-table-list-pointer (cdr tags-table-list-pointer))
521 (or tags-table-list-pointer
522 ;; Wrap around.
523 (setq tags-table-list-pointer tags-table-computed-list))
524
525 (if (eq tags-table-list-pointer tags-table-list-started-at)
526 ;; We have come full circle. No more tables.
527 (setq tags-table-list-pointer nil)
528 ;; Set tags-file-name to the name from the list. It is already expanded.
529 (setq tags-file-name (car tags-table-list-pointer))))
530
97534f32 531;;;###autoload
9708f7fc
RM
532(defun visit-tags-table-buffer (&optional cont)
533 "Select the buffer containing the current tags table.
aab936ad 534If optional arg is a string, visit that file as a tags table.
9708f7fc 535If optional arg is t, visit the next table in `tags-table-list'.
9708f7fc 536If optional arg is the atom `same', don't look for a new table;
b6176f64 537 just select the buffer visiting `tags-file-name'.
47f3c459 538If arg is nil or absent, choose a first buffer from information in
b6176f64 539 `tags-file-name', `tags-table-list', `tags-table-list-pointer'.
9708f7fc 540Returns t if it visits a tags table, or nil if there are no more in the list."
b6176f64
RM
541
542 ;; Set tags-file-name to the tags table file we want to visit.
a3d358c6
RM
543 (cond ((eq cont 'same)
544 ;; Use the ambient value of tags-file-name.
545 (or tags-file-name
b0b3cce2
KH
546 (error "%s"
547 (substitute-command-keys
4fa15f59
RS
548 (concat "No tags table in use; "
549 "use \\[visit-tags-table] to select one")))))
a3d358c6
RM
550
551 ((eq t cont)
552 ;; Find the next table.
553 (if (tags-next-table)
554 ;; Skip over nonexistent files.
555 (while (and (not (or (get-file-buffer tags-file-name)
556 (file-exists-p tags-file-name)))
557 (tags-next-table)))))
558
559 (t
560 ;; Pick a table out of our hat.
561 (tags-table-check-computed-list) ;Get it up to date, we might use it.
562 (setq tags-file-name
563 (or
564 ;; If passed a string, use that.
565 (if (stringp cont)
566 (prog1 cont
567 (setq cont nil)))
568 ;; First, try a local variable.
569 (cdr (assq 'tags-file-name (buffer-local-variables)))
570 ;; Second, try a user-specified function to guess.
571 (and default-tags-table-function
572 (funcall default-tags-table-function))
573 ;; Third, look for a tags table that contains tags for the
574 ;; current buffer's file. If one is found, the lists will
575 ;; be frobnicated, and CONT will be set non-nil so we don't
576 ;; do it below.
577 (and buffer-file-name
83287e5b 578 (or
c3dea9ba
RM
579 ;; First check only tables already in buffers.
580 (tags-table-including buffer-file-name t)
581 ;; Since that didn't find any, now do the
582 ;; expensive version: reading new files.
583 (tags-table-including buffer-file-name nil)))
a3d358c6
RM
584 ;; Fourth, use the user variable tags-file-name, if it is
585 ;; not already in the current list.
586 (and tags-file-name
587 (not (tags-table-list-member tags-file-name
588 tags-table-computed-list))
589 tags-file-name)
590 ;; Fifth, use the user variable giving the table list.
591 ;; Find the first element of the list that actually exists.
592 (let ((list tags-table-list)
593 file)
594 (while (and list
595 (setq file (tags-expand-table-name (car list)))
596 (not (get-file-buffer file))
597 (not (file-exists-p file)))
598 (setq list (cdr list)))
599 (car list))
600 ;; Finally, prompt the user for a file name.
601 (expand-file-name
5b76833f 602 (read-file-name "Visit tags table (default TAGS): "
a3d358c6
RM
603 default-directory
604 "TAGS"
605 t))))))
606
607 ;; Expand the table name into a full file name.
608 (setq tags-file-name (tags-expand-table-name tags-file-name))
609
7e7b42b2 610 (unless (and (eq cont t) (null tags-table-list-pointer))
a3d358c6 611 ;; Verify that tags-file-name names a valid tags table.
229b7986
RM
612 ;; Bind another variable with the value of tags-file-name
613 ;; before we switch buffers, in case tags-file-name is buffer-local.
614 (let ((curbuf (current-buffer))
615 (local-tags-file-name tags-file-name))
616 (if (tags-verify-table local-tags-file-name)
617
618 ;; We have a valid tags table.
619 (progn
620 ;; Bury the tags table buffer so it
621 ;; doesn't get in the user's way.
622 (bury-buffer (current-buffer))
623
624 ;; If this was a new table selection (CONT is nil), make
625 ;; sure tags-table-list includes the chosen table, and
626 ;; update the list pointer variables.
627 (or cont
628 ;; Look in the list for the table we chose.
629 (let ((found (tags-table-list-member
630 local-tags-file-name
631 tags-table-computed-list)))
632 (if found
633 ;; There it is. Just switch to it.
634 (setq tags-table-list-pointer found
635 tags-table-list-started-at found)
636
637 ;; The table is not in the current set.
638 ;; Try to find it in another previously used set.
639 (let ((sets tags-table-set-list))
640 (while (and sets
641 (not (tags-table-list-member
642 local-tags-file-name
643 (car sets))))
644 (setq sets (cdr sets)))
645 (if sets
646 ;; Found in some other set. Switch to that set.
647 (progn
648 (or (memq tags-table-list tags-table-set-list)
649 ;; Save the current list.
650 (setq tags-table-set-list
651 (cons tags-table-list
652 tags-table-set-list)))
653 (setq tags-table-list (car sets)))
654
655 ;; Not found in any existing set.
656 (if (and tags-table-list
657 (or (eq t tags-add-tables)
658 (and tags-add-tables
659 (y-or-n-p
660 (concat "Keep current list of "
661 "tags tables also? ")))))
662 ;; Add it to the current list.
663 (setq tags-table-list (cons local-tags-file-name
664 tags-table-list))
665
666 ;; Make a fresh list, and store the old one.
667 (message "Starting a new list of tags tables")
668 (or (null tags-table-list)
669 (memq tags-table-list tags-table-set-list)
f06df563
RM
670 (setq tags-table-set-list
671 (cons tags-table-list
672 tags-table-set-list)))
dd2cedb9
DL
673 ;; Clear out buffers holding old tables.
674 (dolist (table tags-table-list)
84d51f9b
DL
675 ;; The list can contain items `t'.
676 (if (stringp table)
677 (let ((buffer (find-buffer-visiting table)))
dd2cedb9 678 (if buffer
84d51f9b 679 (kill-buffer buffer)))))
229b7986
RM
680 (setq tags-table-list (list local-tags-file-name))))
681
682 ;; Recompute tags-table-computed-list.
683 (tags-table-check-computed-list)
684 ;; Set the tags table list state variables to start
685 ;; over from tags-table-computed-list.
686 (setq tags-table-list-started-at tags-table-computed-list
687 tags-table-list-pointer
688 tags-table-computed-list)))))
689
690 ;; Return of t says the tags table is valid.
691 t)
692
693 ;; The buffer was not valid. Don't use it again.
694 (set-buffer curbuf)
a3d358c6 695 (kill-local-variable 'tags-file-name)
229b7986 696 (if (eq local-tags-file-name tags-file-name)
e98cc0af 697 (setq tags-file-name nil))
229b7986 698 (error "File %s is not a valid tags table" local-tags-file-name)))))
c6987f0b
RM
699
700(defun tags-reset-tags-tables ()
b533f3c5 701 "Reset tags state to cancel effect of any previous \\[visit-tags-table] or \\[find-tag]."
c6987f0b 702 (interactive)
466886a2
KH
703 ;; Clear out the markers we are throwing away.
704 (let ((i 0))
705 (while (< i find-tag-marker-ring-length)
706 (if (aref (cddr tags-location-ring) i)
707 (set-marker (aref (cddr tags-location-ring) i) nil))
708 (if (aref (cddr find-tag-marker-ring) i)
709 (set-marker (aref (cddr find-tag-marker-ring) i) nil))
710 (setq i (1+ i))))
c6987f0b 711 (setq tags-file-name nil
466886a2
KH
712 tags-location-ring (make-ring find-tag-marker-ring-length)
713 find-tag-marker-ring (make-ring find-tag-marker-ring-length)
c6987f0b
RM
714 tags-table-list nil
715 tags-table-computed-list nil
716 tags-table-computed-list-for nil
717 tags-table-list-pointer nil
718 tags-table-list-started-at nil
719 tags-table-set-list nil))
a128c7a0 720\f
79e01623 721(defun file-of-tag (&optional relative)
ff1f0fa6 722 "Return the file name of the file whose tags point is within.
9708f7fc 723Assumes the tags table is the current buffer.
79e01623
JB
724If RELATIVE is non-nil, file name returned is relative to tags
725table file's directory. If RELATIVE is nil, file name returned
726is complete."
727 (funcall file-of-tag-function relative))
ff1f0fa6 728
c086701a 729;;;###autoload
9708f7fc
RM
730(defun tags-table-files ()
731 "Return a list of files in the current tags table.
83287e5b
RM
732Assumes the tags table is the current buffer. The file names are returned
733as they appeared in the `etags' command that created the table, usually
734without directory names."
a128c7a0
RM
735 (or tags-table-files
736 (setq tags-table-files
737 (funcall tags-table-files-function))))
9708f7fc
RM
738
739(defun tags-included-tables ()
b6176f64
RM
740 "Return a list of tags tables included by the current table.
741Assumes the tags table is the current buffer."
9708f7fc
RM
742 (or tags-included-tables
743 (setq tags-included-tables (funcall tags-included-tables-function))))
744\f
745;; Build tags-completion-table on demand. The single current tags table
746;; and its included tags tables (and their included tables, etc.) have
747;; their tags included in the completion table.
748(defun tags-completion-table ()
749 (or tags-completion-table
f37de644 750 ;; No cached value for this buffer.
9708f7fc 751 (condition-case ()
f37de644
FP
752 (let (current-table combined-table)
753 (message "Making tags completion table for %s..." buffer-file-name)
754 (save-excursion
755 ;; Iterate over the current list of tags tables.
756 (while (visit-tags-table-buffer (and combined-table t))
757 ;; Find possible completions in this table.
758 (setq current-table (funcall tags-completion-table-function))
759 ;; Merge this buffer's completions into the combined table.
760 (if combined-table
761 (mapatoms
762 (lambda (sym) (intern (symbol-name sym) combined-table))
763 current-table)
764 (setq combined-table current-table))))
765 (message "Making tags completion table for %s...done"
766 buffer-file-name)
767 ;; Cache the result a buffer-local variable.
768 (setq tags-completion-table combined-table))
9708f7fc
RM
769 (quit (message "Tags completion table construction aborted.")
770 (setq tags-completion-table nil)))))
771
772;; Completion function for tags. Does normal try-completion,
773;; but builds tags-completion-table on demand.
774(defun tags-complete-tag (string predicate what)
775 (save-excursion
34d51d08
RS
776 ;; If we need to ask for the tag table, allow that.
777 (let ((enable-recursive-minibuffers t))
778 (visit-tags-table-buffer))
9708f7fc
RM
779 (if (eq what t)
780 (all-completions string (tags-completion-table) predicate)
781 (try-completion string (tags-completion-table) predicate))))
a128c7a0 782\f
9708f7fc 783;; Read a tag name from the minibuffer with defaulting and completion.
ff1f0fa6 784(defun find-tag-tag (string)
8a294d90
FP
785 (let* ((completion-ignore-case (if (memq tags-case-fold-search '(t nil))
786 tags-case-fold-search
787 case-fold-search))
788 (default (funcall (or find-tag-default-function
9708f7fc
RM
789 (get major-mode 'find-tag-default-function)
790 'find-tag-default)))
791 (spec (completing-read (if default
ebc6b37c
SM
792 (format "%s (default %s): "
793 (substring string 0 (string-match "[ :]+\\'" string))
794 default)
9708f7fc 795 string)
ab685b6b
RS
796 'tags-complete-tag
797 nil nil nil nil default)))
b6176f64
RM
798 (if (equal spec "")
799 (or default (error "There is no default tag"))
800 spec)))
ff1f0fa6 801
21800cb8
RM
802(defvar last-tag nil
803 "Last tag found by \\[find-tag].")
804
d74e816f
RM
805;; Get interactive args for find-tag{-noselect,-other-window,-regexp}.
806(defun find-tag-interactive (prompt &optional no-default)
926861fb 807 (if (and current-prefix-arg last-tag)
d74e816f
RM
808 (list nil (if (< (prefix-numeric-value current-prefix-arg) 0)
809 '-
810 t))
811 (list (if no-default
812 (read-string prompt)
813 (find-tag-tag prompt)))))
814
c5507689
RS
815(defvar find-tag-history nil)
816
dd2cedb9
DL
817;; Dynamic bondage:
818(eval-when-compile
819 (defvar etags-case-fold-search)
820 (defvar etags-syntax-table))
821
c086701a 822;;;###autoload
9708f7fc
RM
823(defun find-tag-noselect (tagname &optional next-p regexp-p)
824 "Find tag (in current tags table) whose name contains TAGNAME.
f90a6155 825Returns the buffer containing the tag's definition and moves its point there,
9708f7fc
RM
826but does not select the buffer.
827The default for TAGNAME is the expression in the buffer near point.
828
d74e816f
RM
829If second arg NEXT-P is t (interactively, with prefix arg), search for
830another tag that matches the last tagname or regexp used. When there are
831multiple matches for a tag, more exact matches are found first. If NEXT-P
832is the atom `-' (interactively, with prefix arg that is a negative number
833or just \\[negative-argument]), pop back to the previous tag gone to.
ff1f0fa6 834
9708f7fc
RM
835If third arg REGEXP-P is non-nil, treat TAGNAME as a regexp.
836
a70ea557 837A marker representing the point when this command is invoked is pushed
b533f3c5
RS
838onto a ring and may be popped back to with \\[pop-tag-mark].
839Contrast this with the ring of marks gone to by the command.
840
9708f7fc 841See documentation of variable `tags-file-name'."
d74e816f
RM
842 (interactive (find-tag-interactive "Find tag: "))
843
c5507689 844 (setq find-tag-history (cons tagname find-tag-history))
e1cf67b6
GM
845 ;; Save the current buffer's value of `find-tag-hook' before
846 ;; selecting the tags table buffer. For the same reason, save value
847 ;; of `tags-file-name' in case it has a buffer-local value.
dc0274bd 848 (let ((local-find-tag-hook find-tag-hook))
d74e816f
RM
849 (if (eq '- next-p)
850 ;; Pop back to a previous location.
b533f3c5 851 (if (ring-empty-p tags-location-ring)
d74e816f 852 (error "No previous tag locations")
b533f3c5 853 (let ((marker (ring-remove tags-location-ring 0)))
d74e816f
RM
854 (prog1
855 ;; Move to the saved location.
b533f3c5
RS
856 (set-buffer (or (marker-buffer marker)
857 (error "The marked buffer has been deleted")))
d74e816f 858 (goto-char (marker-position marker))
eb8c3be9 859 ;; Kill that marker so it doesn't slow down editing.
d74e816f
RM
860 (set-marker marker nil nil)
861 ;; Run the user's hook. Do we really want to do this for pop?
862 (run-hooks 'local-find-tag-hook))))
b533f3c5
RS
863 ;; Record whence we came.
864 (ring-insert find-tag-marker-ring (point-marker))
926861fb 865 (if (and next-p last-tag)
d74e816f
RM
866 ;; Find the same table we last used.
867 (visit-tags-table-buffer 'same)
868 ;; Pick a table to use.
869 (visit-tags-table-buffer)
870 ;; Record TAGNAME for a future call with NEXT-P non-nil.
871 (setq last-tag tagname))
4cc32db6
RS
872 ;; Record the location so we can pop back to it later.
873 (let ((marker (make-marker)))
874 (save-excursion
875 (set-buffer
876 ;; find-tag-in-order does the real work.
877 (find-tag-in-order
926861fb 878 (if (and next-p last-tag) last-tag tagname)
4cc32db6
RS
879 (if regexp-p
880 find-tag-regexp-search-function
881 find-tag-search-function)
882 (if regexp-p
883 find-tag-regexp-tag-order
83287e5b 884 find-tag-tag-order)
4cc32db6
RS
885 (if regexp-p
886 find-tag-regexp-next-line-after-failure-p
887 find-tag-next-line-after-failure-p)
888 (if regexp-p "matching" "containing")
926861fb 889 (or (not next-p) (not last-tag))))
4cc32db6
RS
890 (set-marker marker (point))
891 (run-hooks 'local-find-tag-hook)
b533f3c5 892 (ring-insert tags-location-ring marker)
4cc32db6 893 (current-buffer))))))
ff1f0fa6 894
c086701a 895;;;###autoload
d74e816f 896(defun find-tag (tagname &optional next-p regexp-p)
9708f7fc
RM
897 "Find tag (in current tags table) whose name contains TAGNAME.
898Select the buffer containing the tag's definition, and move point there.
899The default for TAGNAME is the expression in the buffer around or before point.
c086701a 900
d74e816f
RM
901If second arg NEXT-P is t (interactively, with prefix arg), search for
902another tag that matches the last tagname or regexp used. When there are
903multiple matches for a tag, more exact matches are found first. If NEXT-P
904is the atom `-' (interactively, with prefix arg that is a negative number
905or just \\[negative-argument]), pop back to the previous tag gone to.
ff1f0fa6 906
b533f3c5
RS
907If third arg REGEXP-P is non-nil, treat TAGNAME as a regexp.
908
a70ea557 909A marker representing the point when this command is invoked is pushed
b533f3c5
RS
910onto a ring and may be popped back to with \\[pop-tag-mark].
911Contrast this with the ring of marks gone to by the command.
912
9708f7fc 913See documentation of variable `tags-file-name'."
d74e816f 914 (interactive (find-tag-interactive "Find tag: "))
ebc6b37c
SM
915 (let* ((buf (find-tag-noselect tagname next-p regexp-p))
916 (pos (with-current-buffer buf (point))))
6010664f
SM
917 (condition-case nil
918 (switch-to-buffer buf)
ebc6b37c
SM
919 (error (pop-to-buffer buf)))
920 (goto-char pos)))
9708f7fc 921;;;###autoload (define-key esc-map "." 'find-tag)
ff1f0fa6 922
daa37602 923;;;###autoload
d74e816f 924(defun find-tag-other-window (tagname &optional next-p regexp-p)
9708f7fc 925 "Find tag (in current tags table) whose name contains TAGNAME.
d74e816f
RM
926Select the buffer containing the tag's definition in another window, and
927move point there. The default for TAGNAME is the expression in the buffer
928around or before point.
9708f7fc 929
d74e816f
RM
930If second arg NEXT-P is t (interactively, with prefix arg), search for
931another tag that matches the last tagname or regexp used. When there are
932multiple matches for a tag, more exact matches are found first. If NEXT-P
933is negative (interactively, with prefix arg that is a negative number or
934just \\[negative-argument]), pop back to the previous tag gone to.
9708f7fc 935
b533f3c5
RS
936If third arg REGEXP-P is non-nil, treat TAGNAME as a regexp.
937
a70ea557 938A marker representing the point when this command is invoked is pushed
b533f3c5
RS
939onto a ring and may be popped back to with \\[pop-tag-mark].
940Contrast this with the ring of marks gone to by the command.
941
9708f7fc 942See documentation of variable `tags-file-name'."
d74e816f
RM
943 (interactive (find-tag-interactive "Find tag other window: "))
944
b6176f64
RM
945 ;; This hair is to deal with the case where the tag is found in the
946 ;; selected window's buffer; without the hair, point is moved in both
947 ;; windows. To prevent this, we save the selected window's point before
948 ;; doing find-tag-noselect, and restore it after.
949 (let* ((window-point (window-point (selected-window)))
d74e816f 950 (tagbuf (find-tag-noselect tagname next-p regexp-p))
49693298 951 (tagpoint (progn (set-buffer tagbuf) (point))))
b6176f64
RM
952 (set-window-point (prog1
953 (selected-window)
49693298
JB
954 (switch-to-buffer-other-window tagbuf)
955 ;; We have to set this new window's point; it
956 ;; might already have been displaying a
957 ;; different portion of tagbuf, in which case
958 ;; switch-to-buffer-other-window doesn't set
959 ;; the window's point from the buffer.
960 (set-window-point (selected-window) tagpoint))
b6176f64 961 window-point)))
9708f7fc
RM
962;;;###autoload (define-key ctl-x-4-map "." 'find-tag-other-window)
963
29add8b9 964;;;###autoload
9708f7fc 965(defun find-tag-other-frame (tagname &optional next-p)
d74e816f
RM
966 "Find tag (in current tags table) whose name contains TAGNAME.
967Select the buffer containing the tag's definition in another frame, and
968move point there. The default for TAGNAME is the expression in the buffer
969around or before point.
970
971If second arg NEXT-P is t (interactively, with prefix arg), search for
972another tag that matches the last tagname or regexp used. When there are
973multiple matches for a tag, more exact matches are found first. If NEXT-P
974is negative (interactively, with prefix arg that is a negative number or
975just \\[negative-argument]), pop back to the previous tag gone to.
daa37602 976
b533f3c5
RS
977If third arg REGEXP-P is non-nil, treat TAGNAME as a regexp.
978
a70ea557 979A marker representing the point when this command is invoked is pushed
b533f3c5
RS
980onto a ring and may be popped back to with \\[pop-tag-mark].
981Contrast this with the ring of marks gone to by the command.
982
9708f7fc 983See documentation of variable `tags-file-name'."
d74e816f 984 (interactive (find-tag-interactive "Find tag other frame: "))
9708f7fc
RM
985 (let ((pop-up-frames t))
986 (find-tag-other-window tagname next-p)))
987;;;###autoload (define-key ctl-x-5-map "." 'find-tag-other-frame)
988
daa37602 989;;;###autoload
9708f7fc
RM
990(defun find-tag-regexp (regexp &optional next-p other-window)
991 "Find tag (in current tags table) whose name matches REGEXP.
992Select the buffer containing the tag's definition and move point there.
daa37602 993
d74e816f
RM
994If second arg NEXT-P is t (interactively, with prefix arg), search for
995another tag that matches the last tagname or regexp used. When there are
996multiple matches for a tag, more exact matches are found first. If NEXT-P
997is negative (interactively, with prefix arg that is a negative number or
998just \\[negative-argument]), pop back to the previous tag gone to.
9708f7fc
RM
999
1000If third arg OTHER-WINDOW is non-nil, select the buffer in another window.
1001
a70ea557 1002A marker representing the point when this command is invoked is pushed
b533f3c5
RS
1003onto a ring and may be popped back to with \\[pop-tag-mark].
1004Contrast this with the ring of marks gone to by the command.
1005
9708f7fc 1006See documentation of variable `tags-file-name'."
d74e816f
RM
1007 (interactive (find-tag-interactive "Find tag regexp: " t))
1008 ;; We go through find-tag-other-window to do all the display hair there.
1009 (funcall (if other-window 'find-tag-other-window 'find-tag)
1010 regexp next-p t))
a6125773 1011;;;###autoload (define-key esc-map [?\C-.] 'find-tag-regexp)
b533f3c5
RS
1012
1013;;;###autoload (define-key esc-map "*" 'pop-tag-mark)
1014
1015;;;###autoload
1016(defun pop-tag-mark ()
1017 "Pop back to where \\[find-tag] was last invoked.
1018
1019This is distinct from invoking \\[find-tag] with a negative argument
1020since that pops a stack of markers at which tags were found, not from
1021where they were found."
1022 (interactive)
1023 (if (ring-empty-p find-tag-marker-ring)
1024 (error "No previous locations for find-tag invocation"))
1025 (let ((marker (ring-remove find-tag-marker-ring 0)))
1026 (switch-to-buffer (or (marker-buffer marker)
1027 (error "The marked buffer has been deleted")))
1028 (goto-char (marker-position marker))
1029 (set-marker marker nil nil)))
9708f7fc
RM
1030\f
1031;; Internal tag finding function.
1032
1033;; PATTERN is a string to pass to second arg SEARCH-FORWARD-FUNC, and to
1034;; any member of the function list ORDER (third arg). If ORDER is nil,
1035;; use saved state to continue a previous search.
1036
b7b893bd 1037;; Fourth arg NEXT-LINE-AFTER-FAILURE-P is non-nil if after a failed match,
9708f7fc
RM
1038;; point should be moved to the next line.
1039
b7b893bd
FP
1040;; Fifth arg MATCHING is a string, an English '-ing' word, to be used in
1041;; an error message.
1042
9708f7fc
RM
1043;; Algorithm is as follows. For each qualifier-func in ORDER, go to
1044;; beginning of tags file, and perform inner loop: for each naive match for
1045;; PATTERN found using SEARCH-FORWARD-FUNC, qualify the naive match using
1046;; qualifier-func. If it qualifies, go to the specified line in the
1047;; specified source file and return. Qualified matches are remembered to
1048;; avoid repetition. State is saved so that the loop can be continued.
1049
b7089f3c
RM
1050(defvar tag-lines-already-matched nil) ;matches remembered here between calls
1051
f90a6155
JB
1052(defun find-tag-in-order (pattern
1053 search-forward-func
1054 order
1055 next-line-after-failure-p
1056 matching
1057 first-search)
9708f7fc
RM
1058 (let (file ;name of file containing tag
1059 tag-info ;where to find the tag in FILE
9708f7fc
RM
1060 (first-table t)
1061 (tag-order order)
b7089f3c 1062 (match-marker (make-marker))
9708f7fc 1063 goto-func
ece6e35a
GM
1064 (case-fold-search (if (memq tags-case-fold-search '(nil t))
1065 tags-case-fold-search
1066 case-fold-search))
9708f7fc
RM
1067 )
1068 (save-excursion
b7089f3c
RM
1069
1070 (if first-search
1071 ;; This is the start of a search for a fresh tag.
1072 ;; Clear the list of tags matched by the previous search.
1073 ;; find-tag-noselect has already put us in the first tags table
1074 ;; buffer before we got called.
1075 (setq tag-lines-already-matched nil)
1076 ;; Continuing to search for the tag specified last time.
1077 ;; tag-lines-already-matched lists locations matched in previous
1078 ;; calls so we don't visit the same tag twice if it matches twice
1079 ;; during two passes with different qualification predicates.
1080 ;; Switch to the current tags table buffer.
1081 (visit-tags-table-buffer 'same))
47f3c459 1082
9708f7fc 1083 ;; Get a qualified match.
83287e5b 1084 (catch 'qualified-match-found
47f3c459 1085
b6176f64 1086 ;; Iterate over the list of tags tables.
9708f7fc
RM
1087 (while (or first-table
1088 (visit-tags-table-buffer t))
1089
6218e8c6
RM
1090 (and first-search first-table
1091 ;; Start at beginning of tags file.
1092 (goto-char (point-min)))
5e882a6a 1093
6218e8c6 1094 (setq first-table nil)
9708f7fc 1095
b6176f64 1096 ;; Iterate over the list of ordering predicates.
9708f7fc
RM
1097 (while order
1098 (while (funcall search-forward-func pattern nil t)
1099 ;; Naive match found. Qualify the match.
1100 (and (funcall (car order) pattern)
1101 ;; Make sure it is not a previous qualified match.
b7089f3c
RM
1102 (not (member (set-marker match-marker (save-excursion
1103 (beginning-of-line)
1104 (point)))
1105 tag-lines-already-matched))
9708f7fc
RM
1106 (throw 'qualified-match-found nil))
1107 (if next-line-after-failure-p
1108 (forward-line 1)))
1109 ;; Try the next flavor of match.
1110 (setq order (cdr order))
1111 (goto-char (point-min)))
1112 (setq order tag-order))
1113 ;; We throw out on match, so only get here if there were no matches.
b7089f3c
RM
1114 ;; Clear out the markers we use to avoid duplicate matches so they
1115 ;; don't slow down editting and are immediately available for GC.
1116 (while tag-lines-already-matched
1117 (set-marker (car tag-lines-already-matched) nil nil)
1118 (setq tag-lines-already-matched (cdr tag-lines-already-matched)))
1119 (set-marker match-marker nil nil)
9708f7fc 1120 (error "No %stags %s %s" (if first-search "" "more ")
83287e5b
RM
1121 matching pattern))
1122
9708f7fc
RM
1123 ;; Found a tag; extract location info.
1124 (beginning-of-line)
b7089f3c 1125 (setq tag-lines-already-matched (cons match-marker
9708f7fc
RM
1126 tag-lines-already-matched))
1127 ;; Expand the filename, using the tags table buffer's default-directory.
fb7775eb 1128 ;; We should be able to search for file-name backwards in file-of-tag:
ddc76b00 1129 ;; the beginning-of-line is ok except when positioned on a "file-name" tag.
fb7775eb 1130 (setq file (expand-file-name
ddc76b00
FP
1131 (if (memq (car order) '(tag-exact-file-name-match-p
1132 tag-file-name-match-p
1133 tag-partial-file-name-match-p))
7caf6803 1134 (save-excursion (forward-line 1)
fb7775eb
GM
1135 (file-of-tag))
1136 (file-of-tag)))
9708f7fc
RM
1137 tag-info (funcall snarf-tag-function))
1138
b6176f64 1139 ;; Get the local value in the tags table buffer before switching buffers.
9708f7fc 1140 (setq goto-func goto-tag-location-function)
84406262 1141 (tag-find-file-of-tag-noselect file)
9708f7fc
RM
1142 (widen)
1143 (push-mark)
83287e5b
RM
1144 (funcall goto-func tag-info)
1145
9708f7fc
RM
1146 ;; Return the buffer where the tag was found.
1147 (current-buffer))))
79e01623 1148
84406262 1149(defun tag-find-file-of-tag-noselect (file)
79e01623
JB
1150 ;; Find the right line in the specified file.
1151 ;; If we are interested in compressed-files,
1152 ;; we search files with extensions.
1153 ;; otherwise only the real file.
1154 (let* ((buffer-search-extensions (if (featurep 'jka-compr)
1155 tags-compression-info-list
1156 '("")))
1157 the-buffer
1158 (file-search-extensions buffer-search-extensions))
1159 ;; search a buffer visiting the file with each possible extension
1160 ;; Note: there is a small inefficiency in find-buffer-visiting :
1161 ;; truename is computed even if not needed. Not too sure about this
1162 ;; but I suspect truename computation accesses the disk.
1163 ;; It is maybe a good idea to optimise this find-buffer-visiting.
1164 ;; An alternative would be to use only get-file-buffer
1165 ;; but this looks less "sure" to find the buffer for the file.
1166 (while (and (not the-buffer) buffer-search-extensions)
1167 (setq the-buffer (find-buffer-visiting (concat file (car buffer-search-extensions))))
1168 (setq buffer-search-extensions (cdr buffer-search-extensions)))
1169 ;; if found a buffer but file modified, ensure we re-read !
1170 (if (and the-buffer (not (verify-visited-file-modtime the-buffer)))
1171 (find-file-noselect (buffer-file-name the-buffer)))
1172 ;; if no buffer found, search for files with possible extensions on disk
1173 (while (and (not the-buffer) file-search-extensions)
1174 (if (not (file-exists-p (concat file (car file-search-extensions))))
1175 (setq file-search-extensions (cdr file-search-extensions))
1176 (setq the-buffer (find-file-noselect (concat file (car file-search-extensions))))))
1177 (if (not the-buffer)
1178 (if (featurep 'jka-compr)
1179 (error "File %s (with or without extensions %s) not found" file tags-compression-info-list)
1180 (error "File %s not found" file))
1181 (set-buffer the-buffer))))
1182
84406262
MY
1183(defun tag-find-file-of-tag (file)
1184 (let ((buf (tag-find-file-of-tag-noselect file)))
79e01623
JB
1185 (condition-case nil
1186 (switch-to-buffer buf)
1187 (error (pop-to-buffer buf)))))
9708f7fc
RM
1188\f
1189;; `etags' TAGS file format support.
1190
b6176f64
RM
1191;; If the current buffer is a valid etags TAGS file, give it local values of
1192;; the tags table format variables, and return non-nil.
9708f7fc 1193(defun etags-recognize-tags-table ()
b6176f64 1194 (and (etags-verify-tags-table)
9508896e
JB
1195 ;; It is annoying to flash messages on the screen briefly,
1196 ;; and this message is not useful. -- rms
1197 ;; (message "%s is an `etags' TAGS file" buffer-file-name)
dd2cedb9
DL
1198 (mapc (lambda (elt) (set (make-local-variable (car elt)) (cdr elt)))
1199 '((file-of-tag-function . etags-file-of-tag)
1200 (tags-table-files-function . etags-tags-table-files)
1201 (tags-completion-table-function . etags-tags-completion-table)
1202 (snarf-tag-function . etags-snarf-tag)
1203 (goto-tag-location-function . etags-goto-tag-location)
1204 (find-tag-regexp-search-function . re-search-forward)
1205 (find-tag-regexp-tag-order . (tag-re-match-p))
1206 (find-tag-regexp-next-line-after-failure-p . t)
1207 (find-tag-search-function . search-forward)
1208 (find-tag-tag-order . (tag-exact-file-name-match-p
ddc76b00 1209 tag-file-name-match-p
dd2cedb9 1210 tag-exact-match-p
d30ffe0e 1211 tag-implicit-name-match-p
dd2cedb9
DL
1212 tag-symbol-match-p
1213 tag-word-match-p
fb7775eb 1214 tag-partial-file-name-match-p
dd2cedb9
DL
1215 tag-any-match-p))
1216 (find-tag-next-line-after-failure-p . nil)
1217 (list-tags-function . etags-list-tags)
1218 (tags-apropos-function . etags-tags-apropos)
1219 (tags-included-tables-function . etags-tags-included-tables)
1220 (verify-tags-table-function . etags-verify-tags-table)
1221 ))))
9708f7fc
RM
1222
1223(defun etags-verify-tags-table ()
e7f767c2 1224 "Return non-nil if the current buffer is a valid etags TAGS file."
e4fc4f58 1225 ;; Use eq instead of = in case char-after returns nil.
a1906d51 1226 (eq (char-after (point-min)) ?\f))
9708f7fc 1227
79e01623 1228(defun etags-file-of-tag (&optional relative)
9708f7fc 1229 (save-excursion
5e0b7560 1230 (re-search-backward "\f\n\\([^\n]+\\),[0-9]*\n")
79e01623
JB
1231 (let ((str (buffer-substring (match-beginning 1) (match-end 1))))
1232 (if relative
1233 str
1234 (expand-file-name str
1235 (file-truename default-directory))))))
9708f7fc 1236
aa27fbb4 1237
9708f7fc 1238(defun etags-tags-completion-table ()
46de1c5a 1239 (let ((table (make-vector 511 0))
4f124fb5
EZ
1240 (progress-reporter
1241 (make-progress-reporter
1242 (format "Making tags completion table for %s..." buffer-file-name)
1243 (point-min) (point-max))))
9708f7fc
RM
1244 (save-excursion
1245 (goto-char (point-min))
4a92b718
RM
1246 ;; This monster regexp matches an etags tag line.
1247 ;; \1 is the string to match;
1248 ;; \2 is not interesting;
1249 ;; \3 is the guessed tag name; XXX guess should be better eg DEFUN
eb6a920f
RM
1250 ;; \4 is not interesting;
1251 ;; \5 is the explicitly-specified tag name.
1252 ;; \6 is the line to start searching at;
1253 ;; \7 is the char to start searching at.
4a92b718 1254 (while (re-search-forward
a7885816 1255 "^\\(\\([^\177]+[^-a-zA-Z0-9_+*$:\177]+\\)?\
7e7b42b2
GM
1256\\([-a-zA-Z0-9_+*$?:]+\\)[^-a-zA-Z0-9_+*$?:\177]*\\)\177\
1257\\(\\([^\n\001]+\\)\001\\)?\\([0-9]+\\)?,\\([0-9]+\\)?\n"
4a92b718 1258 nil t)
46de1c5a
MY
1259 (intern (prog1 (if (match-beginning 5)
1260 ;; There is an explicit tag name.
1261 (buffer-substring (match-beginning 5) (match-end 5))
1262 ;; No explicit tag name. Best guess.
1263 (buffer-substring (match-beginning 3) (match-end 3)))
4f124fb5 1264 (progress-reporter-update progress-reporter (point)))
4a92b718 1265 table)))
9708f7fc
RM
1266 table))
1267
79e01623
JB
1268(defun etags-snarf-tag (&optional use-explicit)
1269 (let (tag-text line startpos explicit-start)
83287e5b
RM
1270 (if (save-excursion
1271 (forward-line -1)
1272 (looking-at "\f\n"))
1273 ;; The match was for a source file name, not any tag within a file.
1274 ;; Give text of t, meaning to go exactly to the location we specify,
1275 ;; the beginning of the file.
1276 (setq tag-text t
1277 line nil
a1906d51 1278 startpos (point-min))
83287e5b
RM
1279
1280 ;; Find the end of the tag and record the whole tag text.
1281 (search-forward "\177")
1282 (setq tag-text (buffer-substring (1- (point))
1283 (save-excursion (beginning-of-line)
1284 (point))))
79e01623
JB
1285 ;; If use-explicit is non nil and explicit tag is present, use it as part of
1286 ;; return value. Else just skip it.
1287 (setq explicit-start (point))
1288 (when (and (search-forward "\001" (save-excursion (forward-line 1) (point)) t)
1289 use-explicit)
1290 (setq tag-text (buffer-substring explicit-start (1- (point)))))
1291
1292
83287e5b 1293 (if (looking-at "[0-9]")
027a4b6b
JB
1294 (setq line (string-to-number (buffer-substring
1295 (point)
1296 (progn (skip-chars-forward "0-9")
1297 (point))))))
83287e5b
RM
1298 (search-forward ",")
1299 (if (looking-at "[0-9]")
027a4b6b
JB
1300 (setq startpos (string-to-number (buffer-substring
1301 (point)
1302 (progn (skip-chars-forward "0-9")
1303 (point)))))))
9708f7fc
RM
1304 ;; Leave point on the next line of the tags file.
1305 (forward-line 1)
e1dec509
RM
1306 (cons tag-text (cons line startpos))))
1307
1308;; TAG-INFO is a cons (TEXT LINE . POSITION) where TEXT is the initial part
1309;; of a line containing the tag and POSITION is the character position of
83287e5b
RM
1310;; TEXT within the file (starting from 1); LINE is the line number. If
1311;; TEXT is t, it means the tag refers to exactly LINE or POSITION
1312;; (whichever is present, LINE having preference, no searching. Either
e1dec509
RM
1313;; LINE or POSITION may be nil; POSITION is used if present. If the tag
1314;; isn't exactly at the given position then look around that position using
1315;; a search window which expands until it hits the start of file.
9708f7fc 1316(defun etags-goto-tag-location (tag-info)
e1dec509 1317 (let ((startpos (cdr (cdr tag-info)))
a0f09378 1318 (line (car (cdr tag-info)))
83287e5b
RM
1319 offset found pat)
1320 (if (eq (car tag-info) t)
1321 ;; Direct file tag.
1322 (cond (line (goto-line line))
a0f09378 1323 (startpos (goto-char startpos))
83287e5b
RM
1324 (t (error "etags.el BUG: bogus direct file tag")))
1325 ;; This constant is 1/2 the initial search window.
1326 ;; There is no sense in making it too small,
1327 ;; since just going around the loop once probably
1328 ;; costs about as much as searching 2000 chars.
1329 (setq offset 1000
1330 found nil
1331 pat (concat (if (eq selective-display t)
1332 "\\(^\\|\^m\\)" "^")
1333 (regexp-quote (car tag-info))))
1334 ;; The character position in the tags table is 0-origin.
1335 ;; Convert it to a 1-origin Emacs character position.
1336 (if startpos (setq startpos (1+ startpos)))
1337 ;; If no char pos was given, try the given line number.
1338 (or startpos
a0f09378
RM
1339 (if line
1340 (setq startpos (progn (goto-line line)
83287e5b
RM
1341 (point)))))
1342 (or startpos
1343 (setq startpos (point-min)))
1344 ;; First see if the tag is right at the specified location.
1345 (goto-char startpos)
1346 (setq found (looking-at pat))
1347 (while (and (not found)
1348 (progn
1349 (goto-char (- startpos offset))
1350 (not (bobp))))
1351 (setq found
1352 (re-search-forward pat (+ startpos offset) t)
1353 offset (* 3 offset))) ; expand search window
1354 (or found
1355 (re-search-forward pat nil t)
1356 (error "Rerun etags: `%s' not found in %s"
1357 pat buffer-file-name)))
1358 ;; Position point at the right place
1359 ;; if the search string matched an extra Ctrl-m at the beginning.
1360 (and (eq selective-display t)
1361 (looking-at "\^m")
1362 (forward-char 1))
1363 (beginning-of-line)))
9708f7fc
RM
1364
1365(defun etags-list-tags (file)
a1906d51 1366 (goto-char (point-min))
79e01623
JB
1367 (when (re-search-forward (concat "\f\n" "\\(" file "\\)" ",") nil t)
1368 (let ((path (save-excursion (forward-line 1) (file-of-tag)))
1369 ;; Get the local value in the tags table
1370 ;; buffer before switching buffers.
1371 (goto-func goto-tag-location-function)
1372 tag tag-info pt)
9708f7fc
RM
1373 (forward-line 1)
1374 (while (not (or (eobp) (looking-at "\f")))
79e01623
JB
1375 (setq tag-info (save-excursion (funcall snarf-tag-function t))
1376 tag (car tag-info)
1377 pt (with-current-buffer standard-output (point)))
1378 (princ tag)
1379 (when (= (aref tag 0) ?\() (princ " ...)"))
1380 (with-current-buffer standard-output
1381 (make-text-button pt (point)
1382 'tag-info tag-info
1383 'file-path path
1384 'goto-func goto-func
1385 'action (lambda (button)
1386 (let ((tag-info (button-get button 'tag-info))
1387 (goto-func (button-get button 'goto-func)))
84406262 1388 (tag-find-file-of-tag (button-get button 'file-path))
79e01623
JB
1389 (widen)
1390 (funcall goto-func tag-info)))
1391 'face 'tags-tag-face
1392 'type 'button))
9708f7fc 1393 (terpri)
274d013d 1394 (forward-line 1))
79e01623 1395 t)))
9708f7fc 1396
7e7b42b2
GM
1397(defmacro tags-with-face (face &rest body)
1398 "Execute BODY, give output to `standard-output' face FACE."
06adf6b1 1399 (let ((pp (make-symbol "start")))
286c138d 1400 `(let ((,pp (with-current-buffer standard-output (point))))
7e7b42b2 1401 ,@body
286c138d 1402 (put-text-property ,pp (with-current-buffer standard-output (point))
7e7b42b2
GM
1403 'face ,face standard-output))))
1404
1405(defun etags-tags-apropos-additional (regexp)
1406 "Display tags matching REGEXP from `tags-apropos-additional-actions'."
1407 (with-current-buffer standard-output
1408 (dolist (oba tags-apropos-additional-actions)
1409 (princ "\n\n")
1410 (tags-with-face 'highlight (princ (car oba)))
1411 (princ":\n\n")
79e01623 1412 (let* ((beg (point))
7e7b42b2
GM
1413 (symbs (car (cddr oba)))
1414 (ins-symb (lambda (sy)
1415 (let ((sn (symbol-name sy)))
1416 (when (string-match regexp sn)
79e01623
JB
1417 (make-text-button (point)
1418 (progn (princ sy) (point))
1419 'action-internal(cadr oba)
1420 'action (lambda (button) (funcall
1421 (button-get button 'action-internal)
1422 (button-get button 'item)))
1423 'item sn
1424 'face tags-tag-face
1425 'type 'button)
7e7b42b2
GM
1426 (terpri))))))
1427 (when (symbolp symbs)
1428 (if (boundp symbs)
1429 (setq symbs (symbol-value symbs))
1430 (insert "symbol `" (symbol-name symbs) "' has no value\n")
1431 (setq symbs nil)))
1432 (if (vectorp symbs)
1433 (mapatoms ins-symb symbs)
1434 (dolist (sy symbs)
1435 (funcall ins-symb (car sy))))
1436 (sort-lines nil beg (point))))))
1437
9708f7fc 1438(defun etags-tags-apropos (string)
7e7b42b2
GM
1439 (when tags-apropos-verbose
1440 (princ "Tags in file `")
1441 (tags-with-face 'highlight (princ buffer-file-name))
1442 (princ "':\n\n"))
a1906d51 1443 (goto-char (point-min))
4f124fb5
EZ
1444 (let ((progress-reporter (make-progress-reporter
1445 (format "Making tags apropos buffer for `%s'..."
1446 string)
1447 (point-min) (point-max))))
31f98a93 1448 (while (re-search-forward string nil t)
4f124fb5 1449 (progress-reporter-update progress-reporter (point))
31f98a93
MY
1450 (beginning-of-line)
1451
1452 (let* ( ;; Get the local value in the tags table
1453 ;; buffer before switching buffers.
1454 (goto-func goto-tag-location-function)
1455 (tag-info (save-excursion (funcall snarf-tag-function)))
1456 (tag (if (eq t (car tag-info)) nil (car tag-info)))
1457 (file-path (save-excursion (if tag (file-of-tag)
7caf6803 1458 (save-excursion (forward-line 1)
31f98a93
MY
1459 (file-of-tag)))))
1460 (file-label (if tag (file-of-tag t)
7caf6803 1461 (save-excursion (forward-line 1)
31f98a93
MY
1462 (file-of-tag t))))
1463 (pt (with-current-buffer standard-output (point))))
1464 (if tag
1465 (progn
1466 (princ (format "[%s]: " file-label))
1467 (princ tag)
1468 (when (= (aref tag 0) ?\() (princ " ...)"))
1469 (with-current-buffer standard-output
1470 (make-text-button pt (point)
1471 'tag-info tag-info
1472 'file-path file-path
1473 'goto-func goto-func
1474 'action (lambda (button)
1475 (let ((tag-info (button-get button 'tag-info))
1476 (goto-func (button-get button 'goto-func)))
1477 (tag-find-file-of-tag (button-get button 'file-path))
1478 (widen)
1479 (funcall goto-func tag-info)))
1480 'face 'tags-tag-face
1481 'type 'button)))
1482 (princ (format "- %s" file-label))
1483 (with-current-buffer standard-output
1484 (make-text-button pt (point)
1485 'file-path file-path
1486 'action (lambda (button)
1487 (tag-find-file-of-tag (button-get button 'file-path))
1488 ;; Get the local value in the tags table
1489 ;; buffer before switching buffers.
1490 (goto-char (point-min)))
1491 'face 'tags-tag-face
1492 'type 'button))
1493 ))
1494 (terpri)
1495 (forward-line 1))
1496 (message nil))
7e7b42b2 1497 (when tags-apropos-verbose (princ "\n")))
9708f7fc
RM
1498
1499(defun etags-tags-table-files ()
1500 (let ((files nil)
1501 beg)
1502 (goto-char (point-min))
1503 (while (search-forward "\f\n" nil t)
1504 (setq beg (point))
610c25c1
RS
1505 (end-of-line)
1506 (skip-chars-backward "^," beg)
1507 (or (looking-at "include$")
83287e5b 1508 (setq files (cons (buffer-substring beg (1- (point))) files))))
9708f7fc
RM
1509 (nreverse files)))
1510
1511(defun etags-tags-included-tables ()
1512 (let ((files nil)
1513 beg)
1514 (goto-char (point-min))
1515 (while (search-forward "\f\n" nil t)
1516 (setq beg (point))
610c25c1
RS
1517 (end-of-line)
1518 (skip-chars-backward "^," beg)
1519 (if (looking-at "include$")
9708f7fc 1520 ;; Expand in the default-directory of the tags table buffer.
610c25c1 1521 (setq files (cons (expand-file-name (buffer-substring beg (1- (point))))
9708f7fc
RM
1522 files))))
1523 (nreverse files)))
1524\f
1525;; Empty tags file support.
1526
b6176f64
RM
1527;; Recognize an empty file and give it local values of the tags table format
1528;; variables which do nothing.
7e7b42b2 1529(defun tags-recognize-empty-tags-table ()
9708f7fc 1530 (and (zerop (buffer-size))
dd2cedb9
DL
1531 (mapc (lambda (sym) (set (make-local-variable sym) 'ignore))
1532 '(tags-table-files-function
1533 tags-completion-table-function
1534 find-tag-regexp-search-function
1535 find-tag-search-function
1536 tags-apropos-function
1537 tags-included-tables-function))
9708f7fc 1538 (set (make-local-variable 'verify-tags-table-function)
7e7b42b2 1539 (lambda () (zerop (buffer-size))))))
9708f7fc 1540\f
7e7b42b2 1541;; Match qualifier functions for tagnames.
2aa9d1be 1542;; These functions assume the etags file format defined in etc/ETAGS.EBNF.
9708f7fc 1543
6218e8c6
RM
1544;; This might be a neat idea, but it's too hairy at the moment.
1545;;(defmacro tags-with-syntax (&rest body)
7e7b42b2 1546;; `(let ((current (current-buffer))
6218e8c6
RM
1547;; (otable (syntax-table))
1548;; (buffer (find-file-noselect (file-of-tag)))
1549;; table)
1550;; (unwind-protect
1551;; (progn
1552;; (set-buffer buffer)
1553;; (setq table (syntax-table))
1554;; (set-buffer current)
1555;; (set-syntax-table table)
7e7b42b2
GM
1556;; ,@body)
1557;; (set-syntax-table otable))))
6218e8c6 1558;;(put 'tags-with-syntax 'edebug-form-spec '(&rest form))
8a4c10dc 1559
2aa9d1be
FP
1560;; exact file name match, i.e. searched tag must match complete file
1561;; name including directories parts if there are some.
1562(defun tag-exact-file-name-match-p (tag)
1563 (and (looking-at ",[0-9\n]")
1564 (save-excursion (backward-char (+ 2 (length tag)))
1565 (looking-at "\f\n"))))
1566;; file name match as above, but searched tag must match the file
1567;; name not including the directories if there are some.
1568(defun tag-file-name-match-p (tag)
1569 (and (looking-at ",[0-9\n]")
1570 (save-excursion (backward-char (1+ (length tag)))
1571 (looking-at "/"))))
1572;; this / to detect we are after a directory separator is ok for unix,
1573;; is there a variable that contains the regexp for directory separator
1574;; on whatever operating system ?
1575;; Looks like ms-win will lose here :).
1576
eb21e7ae 1577;; t if point is at a tag line that matches TAG exactly.
9708f7fc 1578;; point should be just after a string that matches TAG.
6218e8c6 1579(defun tag-exact-match-p (tag)
add3312f 1580 ;; The match is really exact if there is an explicit tag name.
63aeffd5 1581 (or (and (eq (char-after (point)) ?\001)
40ce9268 1582 (eq (char-after (- (point) (length tag) 1)) ?\177))
63aeffd5 1583 ;; We are not on the explicit tag name, but perhaps it follows.
eb21e7ae
RM
1584 (looking-at (concat "[^\177\n]*\177" (regexp-quote tag) "\001"))))
1585
d30ffe0e
FP
1586;; t if point is at a tag line that has an implicit name.
1587;; point should be just after a string that matches TAG.
1588(defun tag-implicit-name-match-p (tag)
1589 ;; Look at the comment of the make_tag function in lib-src/etags.c for
1590 ;; a textual description of the four rules.
1591 (and (string-match "^[^ \t()=,;]+$" tag) ;rule #1
1592 (looking-at "[ \t()=,;]?\177") ;rules #2 and #4
1593 (save-excursion
1594 (backward-char (1+ (length tag)))
1595 (looking-at "[\n \t()=,;]")))) ;rule #3
1596
eb21e7ae
RM
1597;; t if point is at a tag line that matches TAG as a symbol.
1598;; point should be just after a string that matches TAG.
1599(defun tag-symbol-match-p (tag)
1600 (and (looking-at "\\Sw.*\177") (looking-at "\\S_.*\177")
1601 (save-excursion
1602 (backward-char (1+ (length tag)))
1603 (and (looking-at "\\Sw") (looking-at "\\S_")))))
9708f7fc
RM
1604
1605;; t if point is at a tag line that matches TAG as a word.
1606;; point should be just after a string that matches TAG.
1607(defun tag-word-match-p (tag)
1608 (and (looking-at "\\b.*\177")
a0f09378 1609 (save-excursion (backward-char (length tag))
9708f7fc
RM
1610 (looking-at "\\b"))))
1611
1ea98518
SM
1612;; partial file name match, i.e. searched tag must match a substring
1613;; of the file name (potentially including a directory separator).
fb7775eb 1614(defun tag-partial-file-name-match-p (tag)
1f21ea94 1615 (and (looking-at ".*,[0-9\n]")
fb7775eb
GM
1616 (save-excursion (beginning-of-line)
1617 (backward-char 2)
1618 (looking-at "\f\n"))))
5e882a6a 1619
9708f7fc
RM
1620;; t if point is in a tag line with a tag containing TAG as a substring.
1621(defun tag-any-match-p (tag)
1622 (looking-at ".*\177"))
ff1f0fa6 1623
9708f7fc
RM
1624;; t if point is at a tag line that matches RE as a regexp.
1625(defun tag-re-match-p (re)
1626 (save-excursion
1627 (beginning-of-line)
1628 (let ((bol (point)))
1629 (and (search-forward "\177" (save-excursion (end-of-line) (point)) t)
1630 (re-search-backward re bol t)))))
1631\f
580d9298
CY
1632(defcustom tags-loop-revert-buffers 'ask
1633 "Whether the tags-scanning loop should reread changed files.
1634This loop normally reads each file into Emacs, but when a file is
1635already visited, it uses the existing buffer.
1636If this variable is nil, the loop uses the existing buffer even
1637if the file has changed since you visited it.
1638If the value is `ask', the loop offers to revert the buffer.
1639Any other non-nil value means to revert the buffer automatically."
d5792fb2
RS
1640 :type 'boolean
1641 :group 'etags)
1642
c086701a 1643;;;###autoload
9708f7fc
RM
1644(defun next-file (&optional initialize novisit)
1645 "Select next file among files in current tags table.
4f1388fd
RM
1646
1647A first argument of t (prefix arg, if interactive) initializes to the
1648beginning of the list of files in the tags table. If the argument is
1649neither nil nor t, it is evalled to initialize the list of files.
9708f7fc
RM
1650
1651Non-nil second argument NOVISIT means use a temporary buffer
1652 to save time and avoid uninteresting warnings.
1653
1654Value is nil if the file was already visited;
1655if the file was newly read in, the value is the filename."
c20032c4
RS
1656 ;; Make the interactive arg t if there was any prefix arg.
1657 (interactive (list (if current-prefix-arg t)))
4f1388fd
RM
1658 (cond ((not initialize)
1659 ;; Not the first run.
1660 )
1661 ((eq initialize t)
1662 ;; Initialize the list from the tags table.
1663 (save-excursion
1664 ;; Visit the tags table buffer to get its list of files.
1665 (visit-tags-table-buffer)
83287e5b
RM
1666 ;; Copy the list so we can setcdr below, and expand the file
1667 ;; names while we are at it, in this buffer's default directory.
1668 (setq next-file-list (mapcar 'expand-file-name (tags-table-files)))
5c9e49a9
RM
1669 ;; Iterate over all the tags table files, collecting
1670 ;; a complete list of referenced file names.
1671 (while (visit-tags-table-buffer t)
1672 ;; Find the tail of the working list and chain on the new
1673 ;; sublist for this tags table.
1674 (let ((tail next-file-list))
1675 (while (cdr tail)
1676 (setq tail (cdr tail)))
1677 ;; Use a copy so the next loop iteration will not modify the
1678 ;; list later returned by (tags-table-files).
2f14fde6 1679 (if tail
83287e5b
RM
1680 (setcdr tail (mapcar 'expand-file-name (tags-table-files)))
1681 (setq next-file-list (mapcar 'expand-file-name
1682 (tags-table-files))))))))
4f1388fd
RM
1683 (t
1684 ;; Initialize the list by evalling the argument.
1685 (setq next-file-list (eval initialize))))
7e7b42b2 1686 (unless next-file-list
5c9e49a9
RM
1687 (and novisit
1688 (get-buffer " *next-file*")
1689 (kill-buffer " *next-file*"))
4fa15f59 1690 (error "All files processed"))
f042d383 1691 (let* ((next (car next-file-list))
d5792fb2
RS
1692 (buffer (get-file-buffer next))
1693 (new (not buffer)))
f042d383
RM
1694 ;; Advance the list before trying to find the file.
1695 ;; If we get an error finding the file, don't get stuck on it.
1696 (setq next-file-list (cdr next-file-list))
d5792fb2
RS
1697 ;; Optionally offer to revert buffers
1698 ;; if the files have changed on disk.
1699 (and buffer tags-loop-revert-buffers
1700 (not (verify-visited-file-modtime buffer))
580d9298
CY
1701 (or (not (eq tags-loop-revert-buffers 'ask))
1702 noninteractive
1703 (y-or-n-p
1704 (format
1705 (if (buffer-modified-p buffer)
1706 "File %s changed on disk. Discard your edits? "
1707 "File %s changed on disk. Reread from disk? ")
1708 next)))
d5792fb2
RS
1709 (with-current-buffer buffer
1710 (revert-buffer t)))
9708f7fc 1711 (if (not (and new novisit))
f042d383 1712 (set-buffer (find-file-noselect next novisit))
9708f7fc
RM
1713 ;; Like find-file, but avoids random warning messages.
1714 (set-buffer (get-buffer-create " *next-file*"))
1715 (kill-all-local-variables)
1716 (erase-buffer)
f042d383 1717 (setq new next)
9708f7fc 1718 (insert-file-contents new nil))
9708f7fc 1719 new))
ff1f0fa6 1720
9708f7fc
RM
1721(defvar tags-loop-operate nil
1722 "Form for `tags-loop-continue' to eval to change one file.")
1723
0f6b9c32 1724(defvar tags-loop-scan
b0b3cce2
KH
1725 '(error "%s"
1726 (substitute-command-keys
4fa15f59 1727 "No \\[tags-search] or \\[tags-query-replace] in progress"))
9708f7fc
RM
1728 "Form for `tags-loop-continue' to eval to scan one file.
1729If it returns non-nil, this file needs processing by evalling
1730\`tags-loop-operate'. Otherwise, move on to the next file.")
ff1f0fa6 1731
ece6e35a
GM
1732(defun tags-loop-eval (form)
1733 "Evaluate FORM and return its result.
1734Bind `case-fold-search' during the evaluation, depending on the value of
1735`tags-case-fold-search'."
1736 (let ((case-fold-search (if (memq tags-case-fold-search '(t nil))
1737 tags-case-fold-search
1738 case-fold-search)))
1739 (eval form)))
ddc76b00 1740
ece6e35a 1741
c086701a 1742;;;###autoload
ff1f0fa6
JB
1743(defun tags-loop-continue (&optional first-time)
1744 "Continue last \\[tags-search] or \\[tags-query-replace] command.
4f1388fd
RM
1745Used noninteractively with non-nil argument to begin such a command (the
1746argument is passed to `next-file', which see).
78809db7
RM
1747
1748Two variables control the processing we do on each file: the value of
1749`tags-loop-scan' is a form to be executed on each file to see if it is
1750interesting (it returns non-nil if so) and `tags-loop-operate' is a form to
1751evaluate to operate on an interesting file. If the latter evaluates to
1752nil, we exit; otherwise we scan the next file."
ff1f0fa6 1753 (interactive)
9708f7fc 1754 (let (new
565c8985
RS
1755 ;; Non-nil means we have finished one file
1756 ;; and should not scan it again.
1757 file-finished
04528cda 1758 original-point
9708f7fc
RM
1759 (messaged nil))
1760 (while
1761 (progn
1762 ;; Scan files quickly for the first or next interesting one.
04528cda 1763 ;; This starts at point in the current buffer.
565c8985 1764 (while (or first-time file-finished
9708f7fc
RM
1765 (save-restriction
1766 (widen)
ece6e35a 1767 (not (tags-loop-eval tags-loop-scan))))
04528cda
GM
1768 ;; If nothing was found in the previous file, and
1769 ;; that file isn't in a temp buffer, restore point to
1770 ;; where it was.
1771 (when original-point
1772 (goto-char original-point))
1773
565c8985 1774 (setq file-finished nil)
9708f7fc 1775 (setq new (next-file first-time t))
04528cda 1776
9708f7fc
RM
1777 ;; If NEW is non-nil, we got a temp buffer,
1778 ;; and NEW is the file name.
04528cda
GM
1779 (when (or messaged
1780 (and (not first-time)
1781 (> baud-rate search-slow-speed)
1782 (setq messaged t)))
1783 (message "Scanning file %s..." (or new buffer-file-name)))
1784
9708f7fc 1785 (setq first-time nil)
04528cda 1786 (setq original-point (if new nil (point)))
9708f7fc
RM
1787 (goto-char (point-min)))
1788
1789 ;; If we visited it in a temp buffer, visit it now for real.
1790 (if new
1791 (let ((pos (point)))
1792 (erase-buffer)
1793 (set-buffer (find-file-noselect new))
78809db7 1794 (setq new nil) ;No longer in a temp buffer.
9708f7fc 1795 (widen)
04528cda
GM
1796 (goto-char pos))
1797 (push-mark original-point t))
9708f7fc
RM
1798
1799 (switch-to-buffer (current-buffer))
1800
1801 ;; Now operate on the file.
1802 ;; If value is non-nil, continue to scan the next file.
ece6e35a 1803 (tags-loop-eval tags-loop-operate))
565c8985 1804 (setq file-finished t))
9708f7fc
RM
1805 (and messaged
1806 (null tags-loop-operate)
1807 (message "Scanning file %s...found" buffer-file-name))))
9708f7fc 1808;;;###autoload (define-key esc-map "," 'tags-loop-continue)
ff1f0fa6 1809
c086701a 1810;;;###autoload
4f1388fd 1811(defun tags-search (regexp &optional file-list-form)
9708f7fc 1812 "Search through all files listed in tags table for match for REGEXP.
ff1f0fa6
JB
1813Stops when a match is found.
1814To continue searching for next match, use command \\[tags-loop-continue].
1815
9708f7fc 1816See documentation of variable `tags-file-name'."
ff1f0fa6
JB
1817 (interactive "sTags search (regexp): ")
1818 (if (and (equal regexp "")
9708f7fc 1819 (eq (car tags-loop-scan) 're-search-forward)
b6176f64 1820 (null tags-loop-operate))
9708f7fc 1821 ;; Continue last tags-search as if by M-,.
ff1f0fa6 1822 (tags-loop-continue nil)
06adf6b1 1823 (setq tags-loop-scan `(re-search-forward ',regexp nil t)
9708f7fc 1824 tags-loop-operate nil)
4f1388fd 1825 (tags-loop-continue (or file-list-form t))))
ff1f0fa6 1826
c086701a 1827;;;###autoload
c575e658 1828(defun tags-query-replace (from to &optional delimited file-list-form)
056f8a11 1829 "Do `query-replace-regexp' of FROM with TO on all files listed in tags table.
ff1f0fa6 1830Third arg DELIMITED (prefix arg) means replace only word-delimited matches.
056f8a11 1831If you exit (\\[keyboard-quit], RET or q), you can resume the query replace
ff1f0fa6 1832with the command \\[tags-loop-continue].
405b8be3
EZ
1833Fourth arg FILE-LIST-FORM non-nil means initialize the replacement loop.
1834Fifth and sixth arguments START and END are accepted, for compatibility
1835with `query-replace-regexp', and ignored.
ff1f0fa6 1836
c575e658
RS
1837If FILE-LIST-FORM is non-nil, it is a form to evaluate to
1838produce the list of files to search.
1839
1840See also the documentation of the variable `tags-file-name'."
17b9c33c 1841 (interactive (query-replace-read-args "Tags query replace (regexp)" t t))
06adf6b1
SM
1842 (setq tags-loop-scan `(let ,(unless (equal from (downcase from))
1843 '((case-fold-search nil)))
1844 (if (re-search-forward ',from nil t)
1845 ;; When we find a match, move back
1846 ;; to the beginning of it so perform-replace
1847 ;; will see it.
1848 (goto-char (match-beginning 0))))
1849 tags-loop-operate `(perform-replace ',from ',to t t ',delimited))
4f1388fd 1850 (tags-loop-continue (or file-list-form t)))
9708f7fc 1851\f
83287e5b
RM
1852(defun tags-complete-tags-table-file (string predicate what)
1853 (save-excursion
1854 ;; If we need to ask for the tag table, allow that.
1855 (let ((enable-recursive-minibuffers t))
1856 (visit-tags-table-buffer))
1857 (if (eq what t)
725349c8
SM
1858 (all-completions string (tags-table-files) predicate)
1859 (try-completion string (tags-table-files) predicate))))
83287e5b 1860
c086701a 1861;;;###autoload
83287e5b
RM
1862(defun list-tags (file &optional next-match)
1863 "Display list of tags in file FILE.
1864This searches only the first table in the list, and no included tables.
1865FILE should be as it appeared in the `etags' command, usually without a
1866directory specification."
1867 (interactive (list (completing-read "List tags in file: "
1868 'tags-complete-tags-table-file
1869 nil t nil)))
1870 (with-output-to-temp-buffer "*Tags List*"
7e7b42b2
GM
1871 (princ "Tags in file `")
1872 (tags-with-face 'highlight (princ file))
1873 (princ "':\n\n")
83287e5b
RM
1874 (save-excursion
1875 (let ((first-time t)
1876 (gotany nil))
1877 (while (visit-tags-table-buffer (not first-time))
1878 (setq first-time nil)
1879 (if (funcall list-tags-function file)
1880 (setq gotany t)))
1881 (or gotany
7e7b42b2
GM
1882 (error "File %s not in current tags tables" file)))))
1883 (with-current-buffer "*Tags List*"
a6e7bdf1 1884 (require 'apropos)
6ef254ec
RS
1885 (with-no-warnings
1886 (apropos-mode))
79e01623 1887 (setq buffer-read-only t)))
ff1f0fa6 1888
c086701a 1889;;;###autoload
9708f7fc
RM
1890(defun tags-apropos (regexp)
1891 "Display list of all tags in tags table REGEXP matches."
1892 (interactive "sTags apropos (regexp): ")
ff1f0fa6 1893 (with-output-to-temp-buffer "*Tags List*"
7e7b42b2
GM
1894 (princ "Click mouse-2 to follow tags.\n\nTags matching regexp `")
1895 (tags-with-face 'highlight (princ regexp))
1896 (princ "':\n\n")
ff1f0fa6 1897 (save-excursion
47f3c459
RM
1898 (let ((first-time t))
1899 (while (visit-tags-table-buffer (not first-time))
1900 (setq first-time nil)
7e7b42b2
GM
1901 (funcall tags-apropos-function regexp))))
1902 (etags-tags-apropos-additional regexp))
1903 (with-current-buffer "*Tags List*"
7caf6803 1904 (eval-and-compile (require 'apropos))
89228b63
JB
1905 (apropos-mode)
1906 ;; apropos-mode is derived from fundamental-mode and it kills
1907 ;; all local variables.
1908 (setq buffer-read-only t)))
9708f7fc 1909\f
1ea98518 1910;; XXX Kludge interface.
aa228418 1911
36278af3 1912(define-button-type 'tags-select-tags-table
29878150 1913 'action 'select-tags-table-select
36278af3
MY
1914 'help-echo "RET, t or mouse-2: select tags table")
1915
9708f7fc 1916;; XXX If a file is in multiple tables, selection may get the wrong one.
29add8b9 1917;;;###autoload
9708f7fc
RM
1918(defun select-tags-table ()
1919 "Select a tags table file from a menu of those you have already used.
168e43e7 1920The list of tags tables to select from is stored in `tags-table-set-list';
9708f7fc
RM
1921see the doc of that variable if you want to add names to the list."
1922 (interactive)
1923 (pop-to-buffer "*Tags Table List*")
9099b373
GM
1924 (setq buffer-read-only nil
1925 buffer-undo-list t)
9708f7fc 1926 (erase-buffer)
9708f7fc 1927 (let ((set-list tags-table-set-list)
36278af3
MY
1928 (desired-point nil)
1929 b)
7e7b42b2 1930 (when tags-table-list
29878150
SM
1931 (setq desired-point (point-marker))
1932 (setq b (point))
1933 (princ (mapcar 'abbreviate-file-name tags-table-list) (current-buffer))
1934 (make-text-button b (point) 'type 'tags-select-tags-table
1935 'etags-table (car tags-table-list))
7e7b42b2 1936 (insert "\n"))
9708f7fc 1937 (while set-list
7e7b42b2 1938 (unless (eq (car set-list) tags-table-list)
36278af3 1939 (setq b (point))
29878150
SM
1940 (princ (mapcar 'abbreviate-file-name (car set-list)) (current-buffer))
1941 (make-text-button b (point) 'type 'tags-select-tags-table
1942 'etags-table (car (car set-list)))
9708f7fc
RM
1943 (insert "\n"))
1944 (setq set-list (cdr set-list)))
7e7b42b2 1945 (when tags-file-name
29878150
SM
1946 (or desired-point
1947 (setq desired-point (point-marker)))
1948 (setq b (point))
1949 (insert (abbreviate-file-name tags-file-name))
1950 (make-text-button b (point) 'type 'tags-select-tags-table
1951 'etags-table tags-file-name)
7e7b42b2 1952 (insert "\n"))
9708f7fc 1953 (setq set-list (delete tags-file-name
9993b561 1954 (apply 'nconc (cons (copy-sequence tags-table-list)
9708f7fc
RM
1955 (mapcar 'copy-sequence
1956 tags-table-set-list)))))
1957 (while set-list
36278af3 1958 (setq b (point))
29878150
SM
1959 (insert (abbreviate-file-name (car set-list)))
1960 (make-text-button b (point) 'type 'tags-select-tags-table
1961 'etags-table (car set-list))
9708f7fc
RM
1962 (insert "\n")
1963 (setq set-list (delete (car set-list) set-list)))
a1906d51 1964 (goto-char (point-min))
9708f7fc
RM
1965 (insert-before-markers
1966 "Type `t' to select a tags table or set of tags tables:\n\n")
1967 (if desired-point
1968 (goto-char desired-point))
1969 (set-window-start (selected-window) 1 t))
1970 (set-buffer-modified-p nil)
ef90db45
RS
1971 (select-tags-table-mode))
1972
36278af3 1973(defvar select-tags-table-mode-map
29878150
SM
1974 (let ((map (make-sparse-keymap)))
1975 (set-keymap-parent map button-buffer-map)
36278af3
MY
1976 (define-key map "t" 'push-button)
1977 (define-key map " " 'next-line)
1978 (define-key map "\^?" 'previous-line)
1979 (define-key map "n" 'next-line)
1980 (define-key map "p" 'previous-line)
1981 (define-key map "q" 'select-tags-table-quit)
1982 map))
ef90db45 1983
29878150 1984(define-derived-mode select-tags-table-mode fundamental-mode "Select Tags Table"
ef90db45
RS
1985 "Major mode for choosing a current tags table among those already loaded.
1986
1987\\{select-tags-table-mode-map}"
29878150
SM
1988 (setq buffer-read-only t))
1989
1990(defun select-tags-table-select (button)
9708f7fc 1991 "Select the tags table named on this line."
29878150
SM
1992 (interactive (list (or (button-at (line-beginning-position))
1993 (error "No tags table on current line"))))
1994 (let ((name (button-get button 'etags-table)))
9708f7fc
RM
1995 (visit-tags-table name)
1996 (select-tags-table-quit)
1997 (message "Tags table now %s" name)))
49116ac0 1998
9708f7fc
RM
1999(defun select-tags-table-quit ()
2000 "Kill the buffer and delete the selected window."
2001 (interactive)
7591cf05 2002 (quit-window t (selected-window)))
9708f7fc 2003\f
1ea98518 2004;; Note, there is another definition of this function in bindings.el.
9708f7fc
RM
2005;;;###autoload
2006(defun complete-tag ()
2007 "Perform tags completion on the text around point.
83287e5b 2008Completes to the set of names listed in the current tags table.
9708f7fc 2009The string to complete is chosen in the same way as the default
ab13123a 2010for \\[find-tag] (which see)."
9708f7fc 2011 (interactive)
ab13123a
RM
2012 (or tags-table-list
2013 tags-file-name
b0b3cce2
KH
2014 (error "%s"
2015 (substitute-command-keys
4fa15f59 2016 "No tags table loaded; try \\[visit-tags-table]")))
8a294d90
FP
2017 (let ((completion-ignore-case (if (memq tags-case-fold-search '(t nil))
2018 tags-case-fold-search
2019 case-fold-search))
2020 (pattern (funcall (or find-tag-default-function
9708f7fc
RM
2021 (get major-mode 'find-tag-default-function)
2022 'find-tag-default)))
2023 beg
2024 completion)
2025 (or pattern
2026 (error "Nothing to complete"))
2027 (search-backward pattern)
2028 (setq beg (point))
2029 (forward-char (length pattern))
7e7b42b2 2030 (setq completion (tags-complete-tag pattern nil nil))
9708f7fc
RM
2031 (cond ((eq completion t))
2032 ((null completion)
2033 (message "Can't find completion for \"%s\"" pattern)
2034 (ding))
2035 ((not (string= pattern completion))
2036 (delete-region beg (point))
2037 (insert completion))
2038 (t
2039 (message "Making completion list...")
c6d38ae2 2040 (with-output-to-temp-buffer "*Completions*"
9708f7fc 2041 (display-completion-list
f5fab556
MY
2042 (all-completions pattern 'tags-complete-tag nil)
2043 pattern))
9708f7fc 2044 (message "Making completion list...%s" "done")))))
dd2cedb9
DL
2045
2046(dolist (x '("^No tags table in use; use .* to select one$"
2047 "^There is no default tag$"
2048 "^No previous tag locations$"
2049 "^File .* is not a valid tags table$"
2050 "^No \\(more \\|\\)tags \\(matching\\|containing\\) "
2051 "^Rerun etags: `.*' not found in "
2052 "^All files processed$"
2053 "^No .* or .* in progress$"
2054 "^File .* not in current tags tables$"
2055 "^No tags table loaded"
2056 "^Nothing to complete$"))
2057 (add-to-list 'debug-ignored-errors x))
9708f7fc
RM
2058\f
2059(provide 'etags)
e5167999 2060
29878150 2061;; arch-tag: b897c2b5-08f3-4837-b2d3-0e7d6db1b63e
e5167999 2062;;; etags.el ends here