(x_wm_set_size_hint): Always set PWinGravity.
[bpt/emacs.git] / lisp / progmodes / etags.el
CommitLineData
c8472948 1;;; etags.el --- etags facility for Emacs
e5167999 2
20783a2b 3;; Copyright (C) 1985, 1986, 1988, 1989, 1992, 1993, 1994
bf349f36 4;; Free Software Foundation, Inc.
ff1f0fa6 5
3a801d0c
ER
6;; Author: Roland McGrath <roland@gnu.ai.mit.edu>
7;; Keywords: tools
8
ff1f0fa6
JB
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
daa37602 13;; the Free Software Foundation; either version 2, or (at your option)
ff1f0fa6
JB
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs; see the file COPYING. If not, write to
23;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24
e5167999
ER
25;;; Code:
26
c086701a 27;;;###autoload
b6176f64
RM
28(defvar tags-file-name nil
29 "*File name of tags table.
9708f7fc 30To switch to a new tags table, setting this variable is sufficient.
b6176f64 31If you set this variable, do not also set `tags-table-list'.
9708f7fc 32Use the `etags' program to make a tags table file.")
b6176f64 33;; Make M-x set-variable tags-file-name like M-x visit-tags-table.
9ef8b0d6 34;;;###autoload (put 'tags-file-name 'variable-interactive "fVisit tags table: ")
9708f7fc
RM
35
36;;;###autoload
b6176f64 37;; Use `visit-tags-table-buffer' to cycle through tags tables in this list.
9708f7fc 38(defvar tags-table-list nil
b6176f64
RM
39 "*List of file names of tags tables to search.
40An element that is a directory means the file \"TAGS\" in that directory.
41To switch to a new list of tags tables, setting this variable is sufficient.
42If you set this variable, do not also set `tags-file-name'.
43Use the `etags' program to make a tags table file.")
9708f7fc 44
33633b28
RM
45;;;###autoload
46(defvar tags-add-tables 'ask-user
0c37b824
RS
47 "*Control whether to add a new tags table to the current list.
48t means do; nil means don't (always start a new list).
49Any other value means ask the user whether to add a new tags table
5f8cdaf2
RS
50to the current list (as opposed to starting a new list).")
51
9708f7fc 52(defvar tags-table-list-pointer nil
47f3c459
RM
53 "Pointer into `tags-table-list' where the current state of searching is.
54Might instead point into a list of included tags tables.
55Use `visit-tags-table-buffer' to cycle through tags tables in this list.")
56
57(defvar tags-table-list-started-at nil
58 "Pointer into `tags-table-list', where the current search started.")
9708f7fc
RM
59
60(defvar tags-table-parent-pointer-list nil
47f3c459
RM
61 "Saved state of the tags table that included this one.
62Each element is (POINTER . STARTED-AT), giving the values of
63 `tags-table-list-pointer' and `tags-table-list-started-at' from
64 before we moved into the current table.")
9708f7fc
RM
65
66(defvar tags-table-set-list nil
67 "List of sets of tags table which have been used together in the past.
68Each element is a list of strings which are file names.")
69
70;;;###autoload
71(defvar find-tag-hook nil
72 "*Hook to be run by \\[find-tag] after finding a tag. See `run-hooks'.
73The value in the buffer in which \\[find-tag] is done is used,
74not the value in the buffer \\[find-tag] goes to.")
75
76;;;###autoload
77(defvar find-tag-default-function nil
47f3c459
RM
78 "*A function of no arguments used by \\[find-tag] to pick a default tag.
79If nil, and the symbol that is the value of `major-mode'
9708f7fc
RM
80has a `find-tag-default-function' property (see `put'), that is used.
81Otherwise, `find-tag-default' is used.")
82
9708f7fc 83(defvar default-tags-table-function nil
5f8cdaf2
RS
84 "If non-nil, a function to choose a default tags file for a buffer.
85This function receives no arguments and should return the default
86tags table file to use for the current buffer.")
d74e816f
RM
87
88(defvar tags-location-stack nil
89 "List of markers which are locations visited by \\[find-tag].
90Pop back to the last location with \\[negative-argument] \\[find-tag].")
9708f7fc
RM
91\f
92;; Tags table state.
93;; These variables are local in tags table buffers.
ff1f0fa6 94
9708f7fc
RM
95(defvar tag-lines-already-matched nil
96 "List of positions of beginnings of lines within the tags table
97that are already matched.")
ff1f0fa6 98
9708f7fc
RM
99(defvar tags-table-files nil
100 "List of file names covered by current tags table.
101nil means it has not yet been computed; use `tags-table-files' to do so.")
102
103(defvar tags-completion-table nil
104 "Alist of tag names defined in current tags table.")
105
106(defvar tags-included-tables nil
107 "List of tags tables included by the current tags table.")
108
109(defvar next-file-list nil
110 "List of files for \\[next-file] to process.")
111\f
112;; Hooks for file formats.
113
114(defvar tags-table-format-hooks '(etags-recognize-tags-table
9ef8b0d6 115 recognize-empty-tags-table)
9708f7fc
RM
116 "List of functions to be called in a tags table buffer to identify
117the type of tags table. The functions are called in order, with no arguments,
118until one returns non-nil. The function should make buffer-local bindings
119of the format-parsing tags function variables if successful.")
120
121(defvar file-of-tag-function nil
122 "Function to do the work of `file-of-tag' (which see).")
123(defvar tags-table-files-function nil
124 "Function to do the work of `tags-table-files' (which see).")
125(defvar tags-completion-table-function nil
126 "Function to build the tags-completion-table.")
127(defvar snarf-tag-function nil
128 "Function to get info about a matched tag for `goto-tag-location-function'.")
129(defvar goto-tag-location-function nil
130 "Function of to go to the location in the buffer specified by a tag.
131One argument, the tag info returned by `snarf-tag-function'.")
132(defvar find-tag-regexp-search-function nil
133 "Search function passed to `find-tag-in-order' for finding a regexp tag.")
134(defvar find-tag-regexp-tag-order nil
135 "Tag order passed to `find-tag-in-order' for finding a regexp tag.")
136(defvar find-tag-regexp-next-line-after-failure-p nil
137 "Flag passed to `find-tag-in-order' for finding a regexp tag.")
138(defvar find-tag-search-function nil
139 "Search function passed to `find-tag-in-order' for finding a tag.")
140(defvar find-tag-tag-order nil
141 "Tag order passed to `find-tag-in-order' for finding a tag.")
142(defvar find-tag-next-line-after-failure-p nil
143 "Flag passed to `find-tag-in-order' for finding a tag.")
144(defvar list-tags-function nil
145 "Function to do the work of `list-tags' (which see).")
146(defvar tags-apropos-function nil
147 "Function to do the work of `tags-apropos' (which see).")
148(defvar tags-included-tables-function nil
149 "Function to do the work of `tags-included-tables' (which see).")
150(defvar verify-tags-table-function nil
eb8c3be9 151 "Function to return t iff the current buffer contains a valid
9708f7fc
RM
152\(already initialized\) tags file.")
153\f
b6176f64
RM
154;; Initialize the tags table in the current buffer.
155;; Returns non-nil iff it is a valid tags table. On
156;; non-nil return, the tags table state variable are
157;; made buffer-local and initialized to nil.
9708f7fc 158(defun initialize-new-tags-table ()
b6176f64
RM
159 (set (make-local-variable 'tag-lines-already-matched) nil)
160 (set (make-local-variable 'tags-table-files) nil)
161 (set (make-local-variable 'tags-completion-table) nil)
162 (set (make-local-variable 'tags-included-tables) nil)
9708f7fc
RM
163 ;; Value is t if we have found a valid tags table buffer.
164 (let ((hooks tags-table-format-hooks))
165 (while (and hooks
166 (not (funcall (car hooks))))
167 (setq hooks (cdr hooks)))
168 hooks))
ff1f0fa6 169
c086701a 170;;;###autoload
9708f7fc
RM
171(defun visit-tags-table (file &optional local)
172 "Tell tags commands to use tags table file FILE.
ff1f0fa6 173FILE should be the name of a file created with the `etags' program.
9708f7fc
RM
174A directory name is ok too; it means file TAGS in that directory.
175
176Normally \\[visit-tags-table] sets the global value of `tags-file-name'.
177With a prefix arg, set the buffer-local value instead.
178When you find a tag with \\[find-tag], the buffer it finds the tag
179in is given a local value of this variable which is the name of the tags
180file the tag was in."
ff1f0fa6
JB
181 (interactive (list (read-file-name "Visit tags table: (default TAGS) "
182 default-directory
9708f7fc
RM
183 (expand-file-name "TAGS"
184 default-directory)
185 t)
186 current-prefix-arg))
b6176f64
RM
187 ;; Calling visit-tags-table-buffer with tags-file-name set to FILE will
188 ;; initialize a buffer for FILE and set tags-file-name to the
189 ;; fully-expanded name.
9806213d
RM
190 (let ((tags-file-name file))
191 (save-excursion
47f3c459 192 (or (visit-tags-table-buffer 'same)
9806213d
RM
193 (signal 'file-error (list "Visiting tags table"
194 "file does not exist"
195 file)))
b6176f64 196 ;; Set FILE to the expanded name.
9806213d 197 (setq file tags-file-name)))
9708f7fc 198 (if local
b6176f64 199 ;; Set the local value of tags-file-name.
47f3c459 200 (set (make-local-variable 'tags-file-name) file)
b6176f64 201 ;; Set the global value of tags-file-name.
9806213d
RM
202 (setq-default tags-file-name file)))
203
204;; Move tags-table-list-pointer along and set tags-file-name.
b6176f64 205;; If NO-INCLUDES is non-nil, ignore included tags tables.
9806213d 206;; Returns nil when out of tables.
b6176f64
RM
207(defun tags-next-table (&optional no-includes)
208 ;; Do we have any included tables?
209 (if (and (not no-includes)
210 (visit-tags-table-buffer 'same)
211 (tags-included-tables))
212
213 ;; Move into the included tags tables.
214 (setq tags-table-parent-pointer-list
215 ;; Save the current state of what table we are in.
216 (cons (cons tags-table-list-pointer tags-table-list-started-at)
217 tags-table-parent-pointer-list)
218 ;; Start the pointer in the list of included tables.
219 tags-table-list-pointer tags-included-tables
220 tags-table-list-started-at tags-included-tables)
221
222 ;; No included tables. Go to the next table in the list.
223 (setq tags-table-list-pointer
224 (cdr tags-table-list-pointer))
225 (or tags-table-list-pointer
226 ;; Wrap around.
227 (setq tags-table-list-pointer tags-table-list))
228
229 (if (eq tags-table-list-pointer tags-table-list-started-at)
230 ;; We have come full circle. No more tables.
231 (if tags-table-parent-pointer-list
232 ;; Pop back to the tags table which includes this one.
233 (progn
234 ;; Restore the state variables.
235 (setq tags-table-list-pointer
236 (car (car tags-table-parent-pointer-list))
237 tags-table-list-started-at
238 (cdr (car tags-table-parent-pointer-list))
239 tags-table-parent-pointer-list
240 (cdr tags-table-parent-pointer-list))
241 ;; Recurse to skip to the next table after the parent.
242 (tags-next-table t))
243 ;; All out of tags tables.
244 (setq tags-table-list-pointer nil))))
245
246 (and tags-table-list-pointer
247 ;; Set tags-file-name to the fully-expanded name.
248 (setq tags-file-name
249 (tags-expand-table-name (car tags-table-list-pointer)))))
250
251;; Expand tags table name FILE into a complete file name.
47f3c459 252(defun tags-expand-table-name (file)
47f3c459
RM
253 (setq file (expand-file-name file))
254 (if (file-directory-p file)
255 (expand-file-name "TAGS" file)
256 file))
257
b6176f64
RM
258;; Return the cdr of LIST (default: tags-table-list) whose car
259;; is equal to FILE after tags-expand-table-name on both sides.
47f3c459
RM
260(defun tags-table-list-member (file &optional list)
261 (or list
262 (setq list tags-table-list))
263 (setq file (tags-expand-table-name file))
264 (while (and list
265 (not (string= file (tags-expand-table-name (car list)))))
266 (setq list (cdr list)))
267 list)
268
a5024e2a
RS
269;; Local var in visit-tags-table-buffer-cont
270;; which is set by tags-table-including.
271(defvar visit-tags-table-buffer-cont)
272
47f3c459 273;; Subroutine of visit-tags-table-buffer. Frobs its local vars.
41e7816a
RM
274;; Search TABLES for one that has tags for THIS-FILE. Recurses on
275;; included tables. Returns the tail of TABLES (or of an inner
276;; included list) whose car is a table listing THIS-FILE. If
277;; CORE-ONLY is non-nil, check only tags tables that are already in
278;; buffers--don't visit any new files.
279(defun tags-table-including (this-file tables core-only &optional recursing)
47f3c459 280 (let ((found nil))
b6176f64 281 ;; Loop over TABLES, looking for one containing tags for THIS-FILE.
47f3c459
RM
282 (while (and (not found)
283 tables)
284 (let ((tags-file-name (tags-expand-table-name (car tables))))
285 (if (or (get-file-buffer tags-file-name)
41e7816a
RM
286 (and (not core-only)
287 (file-exists-p tags-file-name)))
47f3c459
RM
288 (progn
289 ;; Select the tags table buffer and get the file list up to date.
290 (visit-tags-table-buffer 'same)
291 (or tags-table-files
292 (setq tags-table-files
293 (funcall tags-table-files-function)))
294
295 (cond ((member this-file tags-table-files)
296 ;; Found it.
297 (setq found tables))
298
299 ((tags-included-tables)
b6176f64 300 ;; This table has included tables. Check them.
47f3c459
RM
301 (let ((old tags-table-parent-pointer-list))
302 (unwind-protect
303 (progn
304 (or recursing
305 ;; At top level (not in an included tags
306 ;; table), set the list to nil so we can
307 ;; collect just the elts from this run.
308 (setq tags-table-parent-pointer-list nil))
309 (setq found
b6176f64 310 ;; Recurse on the list of included tables.
47f3c459
RM
311 (tags-table-including this-file
312 tags-included-tables
41e7816a 313 core-only
47f3c459
RM
314 t))
315 (if found
b6176f64
RM
316 ;; One of them lists THIS-FILE.
317 ;; Set the table list state variables to move
318 ;; us inside the list of included tables.
319 (setq tags-table-parent-pointer-list
320 (cons
321 (cons tags-table-list-pointer
322 tags-table-list-started-at)
323 tags-table-parent-pointer-list)
324 tags-table-list-pointer found
325 tags-table-list-started-at found
a5024e2a 326 ;; Set a local variable of
b6176f64
RM
327 ;; our caller, visit-tags-table-buffer.
328 ;; Set it so we won't frob lists later.
a5024e2a
RS
329 visit-tags-table-buffer-cont
330 'included)))
47f3c459 331 (or recursing
b6176f64
RM
332 ;; tags-table-parent-pointer-list now describes
333 ;; the path of included tables taken by recursive
334 ;; invocations of this function. The recursive
335 ;; calls have consed onto the front of the list,
336 ;; so it is now outermost first. We want it
337 ;; innermost first, so reverse it. Then append
338 ;; the old list (from before we were called the
339 ;; outermost time), to get the complete current
340 ;; state of included tables.
47f3c459
RM
341 (setq tags-table-parent-pointer-list
342 (nconc (nreverse
343 tags-table-parent-pointer-list)
344 old))))))))))
345 (setq tables (cdr tables)))
346 found))
9708f7fc
RM
347
348(defun visit-tags-table-buffer (&optional cont)
349 "Select the buffer containing the current tags table.
350If optional arg is t, visit the next table in `tags-table-list'.
9708f7fc 351If optional arg is the atom `same', don't look for a new table;
b6176f64 352 just select the buffer visiting `tags-file-name'.
47f3c459 353If arg is nil or absent, choose a first buffer from information in
b6176f64 354 `tags-file-name', `tags-table-list', `tags-table-list-pointer'.
9708f7fc 355Returns t if it visits a tags table, or nil if there are no more in the list."
b6176f64
RM
356
357 ;; Set tags-file-name to the tags table file we want to visit.
a5024e2a
RS
358 (let ((visit-tags-table-buffer-cont cont))
359 (cond ((eq visit-tags-table-buffer-cont 'same)
360 ;; Use the ambient value of tags-file-name.
361 (or tags-file-name
362 (error (substitute-command-keys
363 (concat "No tags table in use! "
20783a2b 364 "Use \\[visit-tags-table] to select one.")))))
a5024e2a
RS
365
366 (visit-tags-table-buffer-cont
367 ;; Find the next table.
368 (if (tags-next-table)
369 ;; Skip over nonexistent files.
20783a2b
RM
370 (let (file)
371 (while (and (setq file
372 (tags-expand-table-name tags-file-name))
a5024e2a
RS
373 (not (or (get-file-buffer file)
374 (file-exists-p file))))
20783a2b 375 (tags-next-table)))))
a5024e2a
RS
376
377 (t
378 ;; Pick a table out of our hat.
379 (setq tags-file-name
380 (or
381 ;; First, try a local variable.
382 (cdr (assq 'tags-file-name (buffer-local-variables)))
383 ;; Second, try a user-specified function to guess.
384 (and default-tags-table-function
385 (funcall default-tags-table-function))
386 ;; Third, look for a tags table that contains
387 ;; tags for the current buffer's file.
388 ;; If one is found, the lists will be frobnicated,
389 ;; and VISIT-TAGS-TABLE-BUFFER-CONT
390 ;; will be set non-nil so we don't do it below.
391 (car (or
392 ;; First check only tables already in buffers.
393 (save-excursion (tags-table-including buffer-file-name
274d013d
RS
394 tags-table-list
395 t))
a5024e2a
RS
396 ;; Since that didn't find any, now do the
397 ;; expensive version: reading new files.
398 (save-excursion (tags-table-including buffer-file-name
399 tags-table-list
400 nil))))
401 ;; Fourth, use the user variable tags-file-name, if it is not
402 ;; already in tags-table-list.
403 (and tags-file-name
404 (not (tags-table-list-member tags-file-name))
405 tags-file-name)
406 ;; Fifth, use the user variable giving the table list.
407 ;; Find the first element of the list that actually exists.
408 (let ((list tags-table-list)
409 file)
410 (while (and list
411 (setq file (tags-expand-table-name (car list)))
412 (not (get-file-buffer file))
413 (not (file-exists-p file)))
414 (setq list (cdr list)))
415 (car list))
416 ;; Finally, prompt the user for a file name.
417 (expand-file-name
418 (read-file-name "Visit tags table: (default TAGS) "
419 default-directory
420 "TAGS"
421 t))))))
422
423 ;; Expand the table name into a full file name.
424 (setq tags-file-name (tags-expand-table-name tags-file-name))
425
20783a2b
RM
426 (if (and (eq visit-tags-table-buffer-cont t)
427 (null tags-table-list-pointer))
a5024e2a
RS
428 ;; All out of tables.
429 nil
430
431 ;; Verify that tags-file-name is a valid tags table.
432 (if (if (get-file-buffer tags-file-name)
433 ;; The file is already in a buffer. Check for the visited file
434 ;; having changed since we last used it.
435 (let (win)
436 (set-buffer (get-file-buffer tags-file-name))
437 (setq win (or verify-tags-table-function
438 (initialize-new-tags-table)))
439 (if (or (verify-visited-file-modtime (current-buffer))
440 (not (yes-or-no-p
441 "Tags file has changed, read new contents? ")))
442 (and win (funcall verify-tags-table-function))
443 (revert-buffer t t)
444 (initialize-new-tags-table)))
445 (set-buffer (find-file-noselect tags-file-name))
446 (or (string= tags-file-name buffer-file-name)
447 ;; find-file-noselect has changed the file name.
448 ;; Propagate the change to tags-file-name and tags-table-list.
449 (let ((tail (member tags-file-name tags-table-list)))
450 (if tail
451 (setcar tail buffer-file-name))
452 (setq tags-file-name buffer-file-name)))
453 (initialize-new-tags-table))
454
455 ;; We have a valid tags table.
456 (progn
457 ;; Bury the tags table buffer so it
458 ;; doesn't get in the user's way.
459 (bury-buffer (current-buffer))
460
20783a2b
RM
461 (if (memq visit-tags-table-buffer-cont '(same nil))
462 ;; Look in the list for the table we chose.
463 (let ((elt (tags-table-list-member tags-file-name)))
464 (or elt
465 ;; The table is not in the current set.
466 ;; Try to find it in another previously used set.
467 (let ((sets tags-table-set-list))
468 (while (and sets
469 (not (setq elt
470 (tags-table-list-member
471 tags-file-name (car sets)))))
472 (setq sets (cdr sets)))
473 (if sets
474 ;; Found in some other set. Switch to that set.
475 (progn
476 (or (memq tags-table-list tags-table-set-list)
477 ;; Save the current list.
478 (setq tags-table-set-list
479 (cons tags-table-list
480 tags-table-set-list)))
481 (setq tags-table-list (car sets)))
482
483 ;; Not found in any existing set.
484 (if (and tags-table-list
33633b28
RM
485 (or (eq t tags-add-tables)
486 (and tags-add-tables
487 (y-or-n-p
488 (concat "Keep current list of "
489 "tags tables also? ")))))
20783a2b
RM
490 ;; Add it to the current list.
491 (setq tags-table-list (cons tags-file-name
492 tags-table-list))
493 ;; Make a fresh list, and store the old one.
494 (message "Starting a new list of tags tables")
a5024e2a 495 (or (memq tags-table-list tags-table-set-list)
a5024e2a
RS
496 (setq tags-table-set-list
497 (cons tags-table-list
498 tags-table-set-list)))
20783a2b
RM
499 (setq tags-table-list (list tags-file-name)))
500 (setq elt tags-table-list))))
501
502 (or visit-tags-table-buffer-cont
503 ;; Set the tags table list state variables to point
504 ;; at the table we want to use first.
505 (setq tags-table-list-started-at elt
506 tags-table-list-pointer elt))))
a5024e2a
RS
507
508 ;; Return of t says the tags table is valid.
509 t)
510
511 ;; The buffer was not valid. Don't use it again.
512 (let ((file tags-file-name))
513 (kill-local-variable 'tags-file-name)
514 (if (eq file tags-file-name)
515 (setq tags-file-name nil)))
516 (error "File %s is not a valid tags table" buffer-file-name)))))
a128c7a0 517\f
ff1f0fa6
JB
518(defun file-of-tag ()
519 "Return the file name of the file whose tags point is within.
9708f7fc
RM
520Assumes the tags table is the current buffer.
521File name returned is relative to tags table file's directory."
522 (funcall file-of-tag-function))
ff1f0fa6 523
c086701a 524;;;###autoload
9708f7fc
RM
525(defun tags-table-files ()
526 "Return a list of files in the current tags table.
b6176f64 527Assumes the tags table is the current buffer.
ff1f0fa6 528File names returned are absolute."
a128c7a0
RM
529 (or tags-table-files
530 (setq tags-table-files
531 (funcall tags-table-files-function))))
9708f7fc
RM
532
533(defun tags-included-tables ()
b6176f64
RM
534 "Return a list of tags tables included by the current table.
535Assumes the tags table is the current buffer."
9708f7fc
RM
536 (or tags-included-tables
537 (setq tags-included-tables (funcall tags-included-tables-function))))
538\f
539;; Build tags-completion-table on demand. The single current tags table
540;; and its included tags tables (and their included tables, etc.) have
541;; their tags included in the completion table.
542(defun tags-completion-table ()
543 (or tags-completion-table
544 (condition-case ()
545 (prog2
546 (message "Making tags completion table for %s..." buffer-file-name)
547 (let ((included (tags-included-tables))
548 (table (funcall tags-completion-table-function)))
549 (save-excursion
b6176f64
RM
550 ;; Iterate over the list of included tables, and combine each
551 ;; included table's completion obarray to the parent obarray.
9708f7fc 552 (while included
b6176f64 553 ;; Visit the buffer.
9708f7fc 554 (let ((tags-file-name (car included)))
47f3c459 555 (visit-tags-table-buffer 'same))
b6176f64 556 ;; Recurse in that buffer to compute its completion table.
9708f7fc 557 (if (tags-completion-table)
b6176f64 558 ;; Combine the tables.
9708f7fc
RM
559 (mapatoms (function
560 (lambda (sym)
561 (intern (symbol-name sym) table)))
562 tags-completion-table))
563 (setq included (cdr included))))
564 (setq tags-completion-table table))
565 (message "Making tags completion table for %s...done"
566 buffer-file-name))
567 (quit (message "Tags completion table construction aborted.")
568 (setq tags-completion-table nil)))))
569
570;; Completion function for tags. Does normal try-completion,
571;; but builds tags-completion-table on demand.
572(defun tags-complete-tag (string predicate what)
573 (save-excursion
34d51d08
RS
574 ;; If we need to ask for the tag table, allow that.
575 (let ((enable-recursive-minibuffers t))
576 (visit-tags-table-buffer))
9708f7fc
RM
577 (if (eq what t)
578 (all-completions string (tags-completion-table) predicate)
579 (try-completion string (tags-completion-table) predicate))))
a128c7a0 580\f
ff1f0fa6
JB
581;; Return a default tag to search for, based on the text at point.
582(defun find-tag-default ()
583 (save-excursion
584 (while (looking-at "\\sw\\|\\s_")
585 (forward-char 1))
9708f7fc
RM
586 (if (or (re-search-backward "\\sw\\|\\s_"
587 (save-excursion (beginning-of-line) (point))
588 t)
589 (re-search-forward "\\(\\sw\\|\\s_\\)+"
590 (save-excursion (end-of-line) (point))
591 t))
592 (progn (goto-char (match-end 0))
ff1f0fa6
JB
593 (buffer-substring (point)
594 (progn (forward-sexp -1)
595 (while (looking-at "\\s'")
596 (forward-char 1))
597 (point))))
598 nil)))
599
9708f7fc 600;; Read a tag name from the minibuffer with defaulting and completion.
ff1f0fa6 601(defun find-tag-tag (string)
9708f7fc
RM
602 (let* ((default (funcall (or find-tag-default-function
603 (get major-mode 'find-tag-default-function)
604 'find-tag-default)))
605 (spec (completing-read (if default
606 (format "%s(default %s) " string default)
607 string)
608 'tags-complete-tag)))
b6176f64
RM
609 (if (equal spec "")
610 (or default (error "There is no default tag"))
611 spec)))
ff1f0fa6 612
21800cb8
RM
613(defvar last-tag nil
614 "Last tag found by \\[find-tag].")
615
d74e816f
RM
616;; Get interactive args for find-tag{-noselect,-other-window,-regexp}.
617(defun find-tag-interactive (prompt &optional no-default)
618 (if current-prefix-arg
619 (list nil (if (< (prefix-numeric-value current-prefix-arg) 0)
620 '-
621 t))
622 (list (if no-default
623 (read-string prompt)
624 (find-tag-tag prompt)))))
625
c086701a 626;;;###autoload
9708f7fc
RM
627(defun find-tag-noselect (tagname &optional next-p regexp-p)
628 "Find tag (in current tags table) whose name contains TAGNAME.
f90a6155 629Returns the buffer containing the tag's definition and moves its point there,
9708f7fc
RM
630but does not select the buffer.
631The default for TAGNAME is the expression in the buffer near point.
632
d74e816f
RM
633If second arg NEXT-P is t (interactively, with prefix arg), search for
634another tag that matches the last tagname or regexp used. When there are
635multiple matches for a tag, more exact matches are found first. If NEXT-P
636is the atom `-' (interactively, with prefix arg that is a negative number
637or just \\[negative-argument]), pop back to the previous tag gone to.
ff1f0fa6 638
9708f7fc
RM
639If third arg REGEXP-P is non-nil, treat TAGNAME as a regexp.
640
641See documentation of variable `tags-file-name'."
d74e816f
RM
642 (interactive (find-tag-interactive "Find tag: "))
643
b6176f64
RM
644 ;; Save the current buffer's value of `find-tag-hook' before selecting the
645 ;; tags table buffer.
9708f7fc 646 (let ((local-find-tag-hook find-tag-hook))
d74e816f
RM
647 (if (eq '- next-p)
648 ;; Pop back to a previous location.
649 (if (null tags-location-stack)
650 (error "No previous tag locations")
651 (let ((marker (car tags-location-stack)))
652 ;; Pop the stack.
653 (setq tags-location-stack (cdr tags-location-stack))
654 (prog1
655 ;; Move to the saved location.
656 (set-buffer (marker-buffer marker))
657 (goto-char (marker-position marker))
eb8c3be9 658 ;; Kill that marker so it doesn't slow down editing.
d74e816f
RM
659 (set-marker marker nil nil)
660 ;; Run the user's hook. Do we really want to do this for pop?
661 (run-hooks 'local-find-tag-hook))))
662 (if next-p
663 ;; Find the same table we last used.
664 (visit-tags-table-buffer 'same)
665 ;; Pick a table to use.
666 (visit-tags-table-buffer)
667 ;; Record TAGNAME for a future call with NEXT-P non-nil.
668 (setq last-tag tagname))
669 (prog1
670 ;; Record the location so we can pop back to it later.
671 (marker-buffer
672 (car
673 (setq tags-location-stack
674 (cons (let ((marker (make-marker)))
675 (save-excursion
676 (set-buffer
677 ;; find-tag-in-order does the real work.
678 (find-tag-in-order
679 (if next-p last-tag tagname)
680 (if regexp-p
681 find-tag-regexp-search-function
682 find-tag-search-function)
683 (if regexp-p
684 find-tag-regexp-tag-order
685 find-tag-tag-order)
686 (if regexp-p
687 find-tag-regexp-next-line-after-failure-p
688 find-tag-next-line-after-failure-p)
689 (if regexp-p "matching" "containing")
690 (not next-p)))
691 (set-marker marker (point))))
692 tags-location-stack))))
693 (run-hooks 'local-find-tag-hook)))))
ff1f0fa6 694
c086701a 695;;;###autoload
d74e816f 696(defun find-tag (tagname &optional next-p regexp-p)
9708f7fc
RM
697 "Find tag (in current tags table) whose name contains TAGNAME.
698Select the buffer containing the tag's definition, and move point there.
699The default for TAGNAME is the expression in the buffer around or before point.
c086701a 700
d74e816f
RM
701If second arg NEXT-P is t (interactively, with prefix arg), search for
702another tag that matches the last tagname or regexp used. When there are
703multiple matches for a tag, more exact matches are found first. If NEXT-P
704is the atom `-' (interactively, with prefix arg that is a negative number
705or just \\[negative-argument]), pop back to the previous tag gone to.
ff1f0fa6 706
9708f7fc 707See documentation of variable `tags-file-name'."
d74e816f
RM
708 (interactive (find-tag-interactive "Find tag: "))
709 (switch-to-buffer (find-tag-noselect tagname next-p regexp-p)))
9708f7fc 710;;;###autoload (define-key esc-map "." 'find-tag)
ff1f0fa6 711
daa37602 712;;;###autoload
d74e816f 713(defun find-tag-other-window (tagname &optional next-p regexp-p)
9708f7fc 714 "Find tag (in current tags table) whose name contains TAGNAME.
d74e816f
RM
715Select the buffer containing the tag's definition in another window, and
716move point there. The default for TAGNAME is the expression in the buffer
717around or before point.
9708f7fc 718
d74e816f
RM
719If second arg NEXT-P is t (interactively, with prefix arg), search for
720another tag that matches the last tagname or regexp used. When there are
721multiple matches for a tag, more exact matches are found first. If NEXT-P
722is negative (interactively, with prefix arg that is a negative number or
723just \\[negative-argument]), pop back to the previous tag gone to.
9708f7fc
RM
724
725See documentation of variable `tags-file-name'."
d74e816f
RM
726 (interactive (find-tag-interactive "Find tag other window: "))
727
b6176f64
RM
728 ;; This hair is to deal with the case where the tag is found in the
729 ;; selected window's buffer; without the hair, point is moved in both
730 ;; windows. To prevent this, we save the selected window's point before
731 ;; doing find-tag-noselect, and restore it after.
732 (let* ((window-point (window-point (selected-window)))
d74e816f 733 (tagbuf (find-tag-noselect tagname next-p regexp-p))
49693298 734 (tagpoint (progn (set-buffer tagbuf) (point))))
b6176f64
RM
735 (set-window-point (prog1
736 (selected-window)
49693298
JB
737 (switch-to-buffer-other-window tagbuf)
738 ;; We have to set this new window's point; it
739 ;; might already have been displaying a
740 ;; different portion of tagbuf, in which case
741 ;; switch-to-buffer-other-window doesn't set
742 ;; the window's point from the buffer.
743 (set-window-point (selected-window) tagpoint))
b6176f64 744 window-point)))
9708f7fc
RM
745;;;###autoload (define-key ctl-x-4-map "." 'find-tag-other-window)
746
29add8b9 747;;;###autoload
9708f7fc 748(defun find-tag-other-frame (tagname &optional next-p)
d74e816f
RM
749 "Find tag (in current tags table) whose name contains TAGNAME.
750Select the buffer containing the tag's definition in another frame, and
751move point there. The default for TAGNAME is the expression in the buffer
752around or before point.
753
754If second arg NEXT-P is t (interactively, with prefix arg), search for
755another tag that matches the last tagname or regexp used. When there are
756multiple matches for a tag, more exact matches are found first. If NEXT-P
757is negative (interactively, with prefix arg that is a negative number or
758just \\[negative-argument]), pop back to the previous tag gone to.
daa37602 759
9708f7fc 760See documentation of variable `tags-file-name'."
d74e816f 761 (interactive (find-tag-interactive "Find tag other frame: "))
9708f7fc
RM
762 (let ((pop-up-frames t))
763 (find-tag-other-window tagname next-p)))
764;;;###autoload (define-key ctl-x-5-map "." 'find-tag-other-frame)
765
daa37602 766;;;###autoload
9708f7fc
RM
767(defun find-tag-regexp (regexp &optional next-p other-window)
768 "Find tag (in current tags table) whose name matches REGEXP.
769Select the buffer containing the tag's definition and move point there.
daa37602 770
d74e816f
RM
771If second arg NEXT-P is t (interactively, with prefix arg), search for
772another tag that matches the last tagname or regexp used. When there are
773multiple matches for a tag, more exact matches are found first. If NEXT-P
774is negative (interactively, with prefix arg that is a negative number or
775just \\[negative-argument]), pop back to the previous tag gone to.
9708f7fc
RM
776
777If third arg OTHER-WINDOW is non-nil, select the buffer in another window.
778
779See documentation of variable `tags-file-name'."
d74e816f
RM
780 (interactive (find-tag-interactive "Find tag regexp: " t))
781 ;; We go through find-tag-other-window to do all the display hair there.
782 (funcall (if other-window 'find-tag-other-window 'find-tag)
783 regexp next-p t))
9708f7fc
RM
784\f
785;; Internal tag finding function.
786
787;; PATTERN is a string to pass to second arg SEARCH-FORWARD-FUNC, and to
788;; any member of the function list ORDER (third arg). If ORDER is nil,
789;; use saved state to continue a previous search.
790
791;; Fourth arg MATCHING is a string, an English '-ing' word, to be used in
792;; an error message.
793
794;; Fifth arg NEXT-LINE-AFTER-FAILURE-P is non-nil if after a failed match,
795;; point should be moved to the next line.
796
797;; Algorithm is as follows. For each qualifier-func in ORDER, go to
798;; beginning of tags file, and perform inner loop: for each naive match for
799;; PATTERN found using SEARCH-FORWARD-FUNC, qualify the naive match using
800;; qualifier-func. If it qualifies, go to the specified line in the
801;; specified source file and return. Qualified matches are remembered to
802;; avoid repetition. State is saved so that the loop can be continued.
803
f90a6155
JB
804(defun find-tag-in-order (pattern
805 search-forward-func
806 order
807 next-line-after-failure-p
808 matching
809 first-search)
9708f7fc
RM
810 (let (file ;name of file containing tag
811 tag-info ;where to find the tag in FILE
812 tags-table-file ;name of tags file
813 (first-table t)
814 (tag-order order)
815 goto-func
816 )
817 (save-excursion
47f3c459
RM
818 (or first-search ;find-tag-noselect has already done it.
819 (visit-tags-table-buffer 'same))
820
9708f7fc
RM
821 ;; Get a qualified match.
822 (catch 'qualified-match-found
47f3c459 823
b6176f64 824 ;; Iterate over the list of tags tables.
9708f7fc
RM
825 (while (or first-table
826 (visit-tags-table-buffer t))
827
828 (if first-search
829 (setq tag-lines-already-matched nil))
830
6218e8c6
RM
831 (and first-search first-table
832 ;; Start at beginning of tags file.
833 (goto-char (point-min)))
834 (setq first-table nil)
9708f7fc
RM
835
836 (setq tags-table-file buffer-file-name)
b6176f64 837 ;; Iterate over the list of ordering predicates.
9708f7fc
RM
838 (while order
839 (while (funcall search-forward-func pattern nil t)
840 ;; Naive match found. Qualify the match.
841 (and (funcall (car order) pattern)
842 ;; Make sure it is not a previous qualified match.
843 ;; Use of `memq' depends on numbers being eq.
844 (not (memq (save-excursion (beginning-of-line) (point))
845 tag-lines-already-matched))
846 (throw 'qualified-match-found nil))
847 (if next-line-after-failure-p
848 (forward-line 1)))
849 ;; Try the next flavor of match.
850 (setq order (cdr order))
851 (goto-char (point-min)))
852 (setq order tag-order))
853 ;; We throw out on match, so only get here if there were no matches.
854 (error "No %stags %s %s" (if first-search "" "more ")
855 matching pattern))
856
857 ;; Found a tag; extract location info.
858 (beginning-of-line)
859 (setq tag-lines-already-matched (cons (point)
860 tag-lines-already-matched))
861 ;; Expand the filename, using the tags table buffer's default-directory.
862 (setq file (expand-file-name (file-of-tag))
863 tag-info (funcall snarf-tag-function))
864
b6176f64 865 ;; Get the local value in the tags table buffer before switching buffers.
9708f7fc
RM
866 (setq goto-func goto-tag-location-function)
867
868 ;; Find the right line in the specified file.
869 (set-buffer (find-file-noselect file))
870 (widen)
871 (push-mark)
872 (funcall goto-func tag-info)
873
874 ;; Give this buffer a local value of tags-file-name.
875 ;; The next time visit-tags-table-buffer is called,
876 ;; it will use the same tags table that found a match in this buffer.
877 (make-local-variable 'tags-file-name)
878 (setq tags-file-name tags-table-file)
879
880 ;; Return the buffer where the tag was found.
881 (current-buffer))))
882\f
883;; `etags' TAGS file format support.
884
b6176f64
RM
885;; If the current buffer is a valid etags TAGS file, give it local values of
886;; the tags table format variables, and return non-nil.
9708f7fc 887(defun etags-recognize-tags-table ()
b6176f64 888 (and (etags-verify-tags-table)
9508896e
JB
889 ;; It is annoying to flash messages on the screen briefly,
890 ;; and this message is not useful. -- rms
891 ;; (message "%s is an `etags' TAGS file" buffer-file-name)
9708f7fc 892 (mapcar (function (lambda (elt)
b6176f64 893 (set (make-local-variable (car elt)) (cdr elt))))
9708f7fc
RM
894 '((file-of-tag-function . etags-file-of-tag)
895 (tags-table-files-function . etags-tags-table-files)
896 (tags-completion-table-function . etags-tags-completion-table)
897 (snarf-tag-function . etags-snarf-tag)
898 (goto-tag-location-function . etags-goto-tag-location)
899 (find-tag-regexp-search-function . re-search-forward)
900 (find-tag-regexp-tag-order . (tag-re-match-p))
7f7436ba 901 (find-tag-regexp-next-line-after-failure-p . t)
9708f7fc
RM
902 (find-tag-search-function . search-forward)
903 (find-tag-tag-order . (tag-exact-match-p tag-word-match-p
904 tag-any-match-p))
905 (find-tag-next-line-after-failure-p . nil)
906 (list-tags-function . etags-list-tags)
907 (tags-apropos-function . etags-tags-apropos)
908 (tags-included-tables-function . etags-tags-included-tables)
909 (verify-tags-table-function . etags-verify-tags-table)
910 ))))
911
b6176f64 912;; Return non-nil iff the current buffer is a valid etags TAGS file.
9708f7fc 913(defun etags-verify-tags-table ()
e4fc4f58
RM
914 ;; Use eq instead of = in case char-after returns nil.
915 (eq (char-after 1) ?\f))
9708f7fc
RM
916
917(defun etags-file-of-tag ()
918 (save-excursion
919 (search-backward "\f\n")
920 (forward-char 2)
921 (buffer-substring (point)
922 (progn (skip-chars-forward "^,") (point)))))
923
924(defun etags-tags-completion-table ()
925 (let ((table (make-vector 511 0)))
926 (save-excursion
927 (goto-char (point-min))
4a92b718
RM
928 ;; This monster regexp matches an etags tag line.
929 ;; \1 is the string to match;
930 ;; \2 is not interesting;
931 ;; \3 is the guessed tag name; XXX guess should be better eg DEFUN
eb6a920f
RM
932 ;; \4 is not interesting;
933 ;; \5 is the explicitly-specified tag name.
934 ;; \6 is the line to start searching at;
935 ;; \7 is the char to start searching at.
4a92b718 936 (while (re-search-forward
bcd54802 937 "^\\(\\(.+[^-a-zA-Z0-9_$]+\\)?\\([-a-zA-Z0-9_$]+\\)\
e1dec509
RM
938\[^-a-zA-Z0-9_$]*\\)\177\\(\\([^\n\001]+\\)\001\\)?\
939\\([0-9]+\\)?,\\([0-9]+\\)?\n"
4a92b718 940 nil t)
eb6a920f 941 (intern (if (match-beginning 5)
4a92b718 942 ;; There is an explicit tag name.
eb6a920f 943 (buffer-substring (match-beginning 5) (match-end 5))
4a92b718
RM
944 ;; No explicit tag name. Best guess.
945 (buffer-substring (match-beginning 3) (match-end 3)))
946 table)))
9708f7fc
RM
947 table))
948
949(defun etags-snarf-tag ()
e1dec509 950 (let (tag-text line startpos)
9708f7fc
RM
951 (search-forward "\177")
952 (setq tag-text (buffer-substring (1- (point))
953 (save-excursion (beginning-of-line)
954 (point))))
eb6a920f
RM
955 ;; Skip explicit tag name if present.
956 (search-forward "\001" (save-excursion (forward-line 1) (point)) t)
e1dec509
RM
957 (if (looking-at "[0-9]")
958 (setq line (string-to-int (buffer-substring
9708f7fc
RM
959 (point)
960 (progn (skip-chars-forward "0-9")
e1dec509
RM
961 (point))))))
962 (search-forward ",")
963 (if (looking-at "[0-9]")
964 (setq startpos (string-to-int (buffer-substring
965 (point)
966 (progn (skip-chars-forward "0-9")
967 (point))))))
9708f7fc
RM
968 ;; Leave point on the next line of the tags file.
969 (forward-line 1)
e1dec509
RM
970 (cons tag-text (cons line startpos))))
971
972;; TAG-INFO is a cons (TEXT LINE . POSITION) where TEXT is the initial part
973;; of a line containing the tag and POSITION is the character position of
974;; TEXT within the file (starting from 1); LINE is the line number. Either
975;; LINE or POSITION may be nil; POSITION is used if present. If the tag
976;; isn't exactly at the given position then look around that position using
977;; a search window which expands until it hits the start of file.
9708f7fc 978(defun etags-goto-tag-location (tag-info)
e1dec509 979 (let ((startpos (cdr (cdr tag-info)))
9708f7fc
RM
980 ;; This constant is 1/2 the initial search window.
981 ;; There is no sense in making it too small,
982 ;; since just going around the loop once probably
983 ;; costs about as much as searching 2000 chars.
984 (offset 1000)
985 (found nil)
cf3623c6
RS
986 (pat (concat (if (eq selective-display t)
987 "\\(^\\|\^m\\)" "^")
988 (regexp-quote (car tag-info)))))
e1dec509
RM
989 ;; If no char pos was given, try the given line number.
990 (or startpos
991 (if (car (cdr tag-info))
992 (setq startpos (progn (goto-line (car (cdr tag-info)))
993 (point)))))
9708f7fc
RM
994 (or startpos
995 (setq startpos (point-min)))
b00f856b
RM
996 ;; First see if the tag is right at the specified location.
997 (goto-char startpos)
998 (setq found (looking-at pat))
9708f7fc
RM
999 (while (and (not found)
1000 (progn
1001 (goto-char (- startpos offset))
1002 (not (bobp))))
1003 (setq found
1004 (re-search-forward pat (+ startpos offset) t)
1005 offset (* 3 offset))) ; expand search window
1006 (or found
1007 (re-search-forward pat nil t)
f9b1c0b2 1008 (error "Rerun etags: `%s' not found in %s"
9708f7fc 1009 pat buffer-file-name)))
280a1a65
RS
1010 ;; Position point at the right place
1011 ;; if the search string matched an extra Ctrl-m at the beginning.
1012 (and (eq selective-display t)
1013 (looking-at "\^m")
1014 (forward-char 1))
9708f7fc
RM
1015 (beginning-of-line))
1016
1017(defun etags-list-tags (file)
1018 (goto-char 1)
1019 (if (not (search-forward (concat "\f\n" file ",") nil t))
1020 nil
1021 (forward-line 1)
1022 (while (not (or (eobp) (looking-at "\f")))
b7277ac6
RM
1023 (let ((tag (buffer-substring (point)
1024 (progn (skip-chars-forward "^\177")
1025 (point)))))
1026 (princ (if (looking-at "[^\n]+\001")
1027 ;; There is an explicit tag name; use that.
1028 (buffer-substring (point)
1029 (progn (skip-chars-forward "^\001")
1030 (point)))
1031 tag)))
9708f7fc 1032 (terpri)
274d013d
RS
1033 (forward-line 1))
1034 t))
9708f7fc
RM
1035
1036(defun etags-tags-apropos (string)
1037 (goto-char 1)
1038 (while (re-search-forward string nil t)
1039 (beginning-of-line)
1040 (princ (buffer-substring (point)
1041 (progn (skip-chars-forward "^\177")
1042 (point))))
1043 (terpri)
1044 (forward-line 1)))
1045
1046(defun etags-tags-table-files ()
1047 (let ((files nil)
1048 beg)
1049 (goto-char (point-min))
1050 (while (search-forward "\f\n" nil t)
1051 (setq beg (point))
1052 (skip-chars-forward "^,\n")
1053 (or (looking-at ",include$")
1054 ;; Expand in the default-directory of the tags table buffer.
1055 (setq files (cons (expand-file-name (buffer-substring beg (point)))
1056 files))))
1057 (nreverse files)))
1058
1059(defun etags-tags-included-tables ()
1060 (let ((files nil)
1061 beg)
1062 (goto-char (point-min))
1063 (while (search-forward "\f\n" nil t)
1064 (setq beg (point))
1065 (skip-chars-forward "^,\n")
1066 (if (looking-at ",include$")
1067 ;; Expand in the default-directory of the tags table buffer.
1068 (setq files (cons (expand-file-name (buffer-substring beg (point)))
1069 files))))
1070 (nreverse files)))
1071\f
1072;; Empty tags file support.
1073
b6176f64
RM
1074;; Recognize an empty file and give it local values of the tags table format
1075;; variables which do nothing.
9708f7fc
RM
1076(defun recognize-empty-tags-table ()
1077 (and (zerop (buffer-size))
1078 (mapcar (function (lambda (sym)
b6176f64 1079 (set (make-local-variable sym) 'ignore)))
9708f7fc
RM
1080 '(tags-table-files-function
1081 tags-completion-table-function
1082 find-tag-regexp-search-function
1083 find-tag-search-function
1084 tags-apropos-function
1085 tags-included-tables-function))
1086 (set (make-local-variable 'verify-tags-table-function)
1087 (function (lambda ()
1088 (zerop (buffer-size)))))))
1089\f
1090;;; Match qualifier functions for tagnames.
b6176f64 1091;;; XXX these functions assume etags file format.
9708f7fc 1092
6218e8c6
RM
1093;; This might be a neat idea, but it's too hairy at the moment.
1094;;(defmacro tags-with-syntax (&rest body)
1095;; (` (let ((current (current-buffer))
1096;; (otable (syntax-table))
1097;; (buffer (find-file-noselect (file-of-tag)))
1098;; table)
1099;; (unwind-protect
1100;; (progn
1101;; (set-buffer buffer)
1102;; (setq table (syntax-table))
1103;; (set-buffer current)
1104;; (set-syntax-table table)
1105;; (,@ body))
1106;; (set-syntax-table otable)))))
1107;;(put 'tags-with-syntax 'edebug-form-spec '(&rest form))
8a4c10dc 1108
9708f7fc
RM
1109;; t if point is at a tag line that matches TAG "exactly".
1110;; point should be just after a string that matches TAG.
6218e8c6 1111(defun tag-exact-match-p (tag)
add3312f
RM
1112 ;; The match is really exact if there is an explicit tag name.
1113 (or (looking-at (concat "[^\177]*\177" (regexp-quote tag) "\001"))
1114 ;; We also call it "exact" if it is surrounded by symbol boundaries.
1115 ;; This is needed because etags does not always generate explicit names.
1116 (and (looking-at "\\Sw.*\177") (looking-at "\\S_.*\177")
1117 (save-excursion
1118 (backward-char (1+ (length tag)))
1119 (and (looking-at "\\Sw") (looking-at "\\S_"))))))
9708f7fc
RM
1120
1121;; t if point is at a tag line that matches TAG as a word.
1122;; point should be just after a string that matches TAG.
1123(defun tag-word-match-p (tag)
1124 (and (looking-at "\\b.*\177")
1125 (save-excursion (backward-char (1+ (length tag)))
1126 (looking-at "\\b"))))
1127
1128;; t if point is in a tag line with a tag containing TAG as a substring.
1129(defun tag-any-match-p (tag)
1130 (looking-at ".*\177"))
ff1f0fa6 1131
9708f7fc
RM
1132;; t if point is at a tag line that matches RE as a regexp.
1133(defun tag-re-match-p (re)
1134 (save-excursion
1135 (beginning-of-line)
1136 (let ((bol (point)))
1137 (and (search-forward "\177" (save-excursion (end-of-line) (point)) t)
1138 (re-search-backward re bol t)))))
1139\f
c086701a 1140;;;###autoload
9708f7fc
RM
1141(defun next-file (&optional initialize novisit)
1142 "Select next file among files in current tags table.
4f1388fd
RM
1143
1144A first argument of t (prefix arg, if interactive) initializes to the
1145beginning of the list of files in the tags table. If the argument is
1146neither nil nor t, it is evalled to initialize the list of files.
9708f7fc
RM
1147
1148Non-nil second argument NOVISIT means use a temporary buffer
1149 to save time and avoid uninteresting warnings.
1150
1151Value is nil if the file was already visited;
1152if the file was newly read in, the value is the filename."
ff1f0fa6 1153 (interactive "P")
4f1388fd
RM
1154 (cond ((not initialize)
1155 ;; Not the first run.
1156 )
1157 ((eq initialize t)
1158 ;; Initialize the list from the tags table.
1159 (save-excursion
1160 ;; Visit the tags table buffer to get its list of files.
1161 (visit-tags-table-buffer)
1162 (setq next-file-list (tags-table-files))))
1163 (t
1164 ;; Initialize the list by evalling the argument.
1165 (setq next-file-list (eval initialize))))
ff1f0fa6 1166 (or next-file-list
9708f7fc 1167 (save-excursion
a128c7a0 1168 ;; Get the files from the next tags table.
974f8dd6 1169 ;; When doing (visit-tags-table-buffer t),
9708f7fc
RM
1170 ;; the tags table buffer must be current.
1171 (if (and (visit-tags-table-buffer 'same)
1172 (visit-tags-table-buffer t))
1173 (setq next-file-list (tags-table-files))
1174 (and novisit
1175 (get-buffer " *next-file*")
1176 (kill-buffer " *next-file*"))
1177 (error "All files processed."))))
1178 (let ((new (not (get-file-buffer (car next-file-list)))))
1179 (if (not (and new novisit))
1180 (set-buffer (find-file-noselect (car next-file-list) novisit))
1181 ;; Like find-file, but avoids random warning messages.
1182 (set-buffer (get-buffer-create " *next-file*"))
1183 (kill-all-local-variables)
1184 (erase-buffer)
1185 (setq new (car next-file-list))
1186 (insert-file-contents new nil))
1187 (setq next-file-list (cdr next-file-list))
1188 new))
ff1f0fa6 1189
9708f7fc
RM
1190(defvar tags-loop-operate nil
1191 "Form for `tags-loop-continue' to eval to change one file.")
1192
0f6b9c32
RM
1193(defvar tags-loop-scan
1194 '(error (substitute-command-keys
1195 "No \\[tags-search] or \\[tags-query-replace] in progress."))
9708f7fc
RM
1196 "Form for `tags-loop-continue' to eval to scan one file.
1197If it returns non-nil, this file needs processing by evalling
1198\`tags-loop-operate'. Otherwise, move on to the next file.")
ff1f0fa6 1199
c086701a 1200;;;###autoload
ff1f0fa6
JB
1201(defun tags-loop-continue (&optional first-time)
1202 "Continue last \\[tags-search] or \\[tags-query-replace] command.
4f1388fd
RM
1203Used noninteractively with non-nil argument to begin such a command (the
1204argument is passed to `next-file', which see).
9708f7fc
RM
1205Two variables control the processing we do on each file:
1206the value of `tags-loop-scan' is a form to be executed on each file
1207to see if it is interesting (it returns non-nil if so)
1208and `tags-loop-operate' is a form to execute to operate on an interesting file
1209If the latter returns non-nil, we exit; otherwise we scan the next file."
ff1f0fa6 1210 (interactive)
9708f7fc
RM
1211 (let (new
1212 (messaged nil))
1213 (while
1214 (progn
1215 ;; Scan files quickly for the first or next interesting one.
1216 (while (or first-time
1217 (save-restriction
1218 (widen)
1219 (not (eval tags-loop-scan))))
1220 (setq new (next-file first-time t))
1221 ;; If NEW is non-nil, we got a temp buffer,
1222 ;; and NEW is the file name.
1223 (if (or messaged
1224 (and (not first-time)
1225 (> baud-rate search-slow-speed)
1226 (setq messaged t)))
1227 (message "Scanning file %s..." (or new buffer-file-name)))
1228 (setq first-time nil)
1229 (goto-char (point-min)))
1230
1231 ;; If we visited it in a temp buffer, visit it now for real.
1232 (if new
1233 (let ((pos (point)))
1234 (erase-buffer)
1235 (set-buffer (find-file-noselect new))
1236 (widen)
1237 (goto-char pos)))
1238
1239 (switch-to-buffer (current-buffer))
1240
1241 ;; Now operate on the file.
1242 ;; If value is non-nil, continue to scan the next file.
1243 (eval tags-loop-operate)))
1244 (and messaged
1245 (null tags-loop-operate)
1246 (message "Scanning file %s...found" buffer-file-name))))
9708f7fc 1247;;;###autoload (define-key esc-map "," 'tags-loop-continue)
ff1f0fa6 1248
c086701a 1249;;;###autoload
4f1388fd 1250(defun tags-search (regexp &optional file-list-form)
9708f7fc 1251 "Search through all files listed in tags table for match for REGEXP.
ff1f0fa6
JB
1252Stops when a match is found.
1253To continue searching for next match, use command \\[tags-loop-continue].
1254
9708f7fc 1255See documentation of variable `tags-file-name'."
ff1f0fa6
JB
1256 (interactive "sTags search (regexp): ")
1257 (if (and (equal regexp "")
9708f7fc 1258 (eq (car tags-loop-scan) 're-search-forward)
b6176f64 1259 (null tags-loop-operate))
9708f7fc 1260 ;; Continue last tags-search as if by M-,.
ff1f0fa6 1261 (tags-loop-continue nil)
9708f7fc
RM
1262 (setq tags-loop-scan
1263 (list 're-search-forward regexp nil t)
1264 tags-loop-operate nil)
4f1388fd 1265 (tags-loop-continue (or file-list-form t))))
ff1f0fa6 1266
c086701a 1267;;;###autoload
4f1388fd 1268(defun tags-query-replace (from to &optional delimited file-list-form)
9708f7fc 1269 "Query-replace-regexp FROM with TO through all files listed in tags table.
ff1f0fa6 1270Third arg DELIMITED (prefix arg) means replace only word-delimited matches.
9708f7fc 1271If you exit (\\[keyboard-quit] or ESC), you can resume the query-replace
ff1f0fa6
JB
1272with the command \\[tags-loop-continue].
1273
9708f7fc
RM
1274See documentation of variable `tags-file-name'."
1275 (interactive
1276 "sTags query replace (regexp): \nsTags query replace %s by: \nP")
29add8b9 1277 (setq tags-loop-scan (list 'prog1
312f637b 1278 (list 'if (list 're-search-forward from nil t)
29add8b9
RM
1279 ;; When we find a match, move back
1280 ;; to the beginning of it so perform-replace
1281 ;; will see it.
1282 '(goto-char (match-beginning 0))))
9708f7fc 1283 tags-loop-operate (list 'perform-replace from to t t delimited))
4f1388fd 1284 (tags-loop-continue (or file-list-form t)))
9708f7fc 1285\f
c086701a 1286;;;###autoload
9708f7fc 1287(defun list-tags (file)
ff1f0fa6 1288 "Display list of tags in file FILE.
274d013d 1289FILE should not contain a directory specification."
76c07747
RM
1290 (interactive (list (completing-read "List tags in file: "
1291 (save-excursion
1292 (visit-tags-table-buffer)
274d013d
RS
1293 (mapcar 'list
1294 (mapcar 'file-name-nondirectory
1295 (tags-table-files))))
76c07747 1296 nil t nil)))
ff1f0fa6
JB
1297 (with-output-to-temp-buffer "*Tags List*"
1298 (princ "Tags in file ")
9708f7fc 1299 (princ file)
ff1f0fa6
JB
1300 (terpri)
1301 (save-excursion
9708f7fc
RM
1302 (let ((first-time t)
1303 (gotany nil))
47f3c459 1304 (while (visit-tags-table-buffer (not first-time))
274d013d 1305 (setq first-time nil)
9708f7fc
RM
1306 (if (funcall list-tags-function file)
1307 (setq gotany t)))
1308 (or gotany
274d013d 1309 (error "File %s not in current tags tables" file))))))
ff1f0fa6 1310
c086701a 1311;;;###autoload
9708f7fc
RM
1312(defun tags-apropos (regexp)
1313 "Display list of all tags in tags table REGEXP matches."
1314 (interactive "sTags apropos (regexp): ")
ff1f0fa6
JB
1315 (with-output-to-temp-buffer "*Tags List*"
1316 (princ "Tags matching regexp ")
9708f7fc 1317 (prin1 regexp)
ff1f0fa6
JB
1318 (terpri)
1319 (save-excursion
47f3c459
RM
1320 (let ((first-time t))
1321 (while (visit-tags-table-buffer (not first-time))
1322 (setq first-time nil)
a32a25f4 1323 (funcall tags-apropos-function regexp))))))
9708f7fc
RM
1324\f
1325;;; XXX Kludge interface.
aa228418 1326
9708f7fc 1327;; XXX If a file is in multiple tables, selection may get the wrong one.
29add8b9 1328;;;###autoload
9708f7fc
RM
1329(defun select-tags-table ()
1330 "Select a tags table file from a menu of those you have already used.
1331The list of tags tables to select from is stored in `tags-table-file-list';
1332see the doc of that variable if you want to add names to the list."
1333 (interactive)
1334 (pop-to-buffer "*Tags Table List*")
1335 (setq buffer-read-only nil)
1336 (erase-buffer)
1337 (setq selective-display t
1338 selective-display-ellipses nil)
1339 (let ((set-list tags-table-set-list)
1340 (desired-point nil))
1341 (if tags-table-list
1342 (progn
1343 (setq desired-point (point-marker))
1344 (princ tags-table-list (current-buffer))
1345 (insert "\C-m")
1346 (prin1 (car tags-table-list) (current-buffer)) ;invisible
1347 (insert "\n")))
1348 (while set-list
1349 (if (eq (car set-list) tags-table-list)
1350 ;; Already printed it.
1351 ()
1352 (princ (car set-list) (current-buffer))
1353 (insert "\C-m")
1354 (prin1 (car (car set-list)) (current-buffer)) ;invisible
1355 (insert "\n"))
1356 (setq set-list (cdr set-list)))
1357 (if tags-file-name
1358 (progn
1359 (or desired-point
1360 (setq desired-point (point-marker)))
1361 (insert tags-file-name "\C-m")
1362 (prin1 tags-file-name (current-buffer)) ;invisible
1363 (insert "\n")))
1364 (setq set-list (delete tags-file-name
1365 (apply 'nconc (cons tags-table-list
1366 (mapcar 'copy-sequence
1367 tags-table-set-list)))))
1368 (while set-list
1369 (insert (car set-list) "\C-m")
1370 (prin1 (car set-list) (current-buffer)) ;invisible
1371 (insert "\n")
1372 (setq set-list (delete (car set-list) set-list)))
1373 (goto-char 1)
1374 (insert-before-markers
1375 "Type `t' to select a tags table or set of tags tables:\n\n")
1376 (if desired-point
1377 (goto-char desired-point))
1378 (set-window-start (selected-window) 1 t))
1379 (set-buffer-modified-p nil)
1380 (setq buffer-read-only t
1381 mode-name "Select Tags Table")
1382 (let ((map (make-sparse-keymap)))
1383 (define-key map "t" 'select-tags-table-select)
1384 (define-key map " " 'next-line)
1385 (define-key map "\^?" 'previous-line)
1386 (define-key map "n" 'next-line)
1387 (define-key map "p" 'previous-line)
1388 (define-key map "q" 'select-tags-table-quit)
1389 (use-local-map map)))
1390
1391(defun select-tags-table-select ()
1392 "Select the tags table named on this line."
1393 (interactive)
1394 (search-forward "\C-m")
1395 (let ((name (read (current-buffer))))
1396 (visit-tags-table name)
1397 (select-tags-table-quit)
1398 (message "Tags table now %s" name)))
49116ac0 1399
9708f7fc
RM
1400(defun select-tags-table-quit ()
1401 "Kill the buffer and delete the selected window."
1402 (interactive)
1403 (kill-buffer (current-buffer))
1404 (or (one-window-p)
1405 (delete-window)))
1406\f
1407;;;###autoload
1408(defun complete-tag ()
1409 "Perform tags completion on the text around point.
1410Completes to the set of names listed in the current tags table.
1411The string to complete is chosen in the same way as the default
ab13123a 1412for \\[find-tag] (which see)."
9708f7fc 1413 (interactive)
ab13123a
RM
1414 (or tags-table-list
1415 tags-file-name
1416 (error (substitute-command-keys
1417 "No tags table loaded. Try \\[visit-tags-table].")))
9708f7fc
RM
1418 (let ((pattern (funcall (or find-tag-default-function
1419 (get major-mode 'find-tag-default-function)
1420 'find-tag-default)))
1421 beg
1422 completion)
1423 (or pattern
1424 (error "Nothing to complete"))
1425 (search-backward pattern)
1426 (setq beg (point))
1427 (forward-char (length pattern))
1428 (setq completion (try-completion pattern 'tags-complete-tag nil))
1429 (cond ((eq completion t))
1430 ((null completion)
1431 (message "Can't find completion for \"%s\"" pattern)
1432 (ding))
1433 ((not (string= pattern completion))
1434 (delete-region beg (point))
1435 (insert completion))
1436 (t
1437 (message "Making completion list...")
1438 (with-output-to-temp-buffer " *Completions*"
1439 (display-completion-list
1440 (all-completions pattern 'tags-complete-tag nil)))
1441 (message "Making completion list...%s" "done")))))
6218e8c6
RM
1442
1443;;;###autoload (define-key esc-map "\t" 'complete-tag)
9708f7fc
RM
1444\f
1445(provide 'etags)
e5167999
ER
1446
1447;;; etags.el ends here