*** empty log message ***
[bpt/emacs.git] / lisp / progmodes / etags.el
CommitLineData
e5167999
ER
1;; etags.el --- etags facility for Emacs
2
c9ed5a47 3;; Copyright (C) 1985, 1986, 1988, 1989, 1992 Free Software Foundation, Inc.
ff1f0fa6 4
3a801d0c
ER
5;; Author: Roland McGrath <roland@gnu.ai.mit.edu>
6;; Keywords: tools
7
ff1f0fa6
JB
8;; This file is part of GNU Emacs.
9
10;; GNU Emacs is free software; you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
daa37602 12;; the Free Software Foundation; either version 2, or (at your option)
ff1f0fa6
JB
13;; any later version.
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
21;; along with GNU Emacs; see the file COPYING. If not, write to
22;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23
e5167999
ER
24;;; Code:
25
c086701a
JB
26;;;###autoload
27(defvar tags-file-name nil "\
9708f7fc
RM
28*File name of tags table.
29To switch to a new tags table, setting this variable is sufficient.
30Use the `etags' program to make a tags table file.")
9ef8b0d6 31;;;###autoload (put 'tags-file-name 'variable-interactive "fVisit tags table: ")
9708f7fc
RM
32
33;;;###autoload
34(defvar tags-table-list nil
35 "*List of names of tags table files which are currently being searched.
36An element of nil means to look for a file \"TAGS\" in the current directory.
37Use `visit-tags-table-buffer' to cycle through tags tables in this list.")
38
39(defvar tags-table-list-pointer nil
40 "Pointer into `tags-table-list', or into a list of included tags tables,
41where the current state of searching is. Use `visit-tags-table-buffer' to
42cycle through tags tables in this list.")
43
44(defvar tags-table-parent-pointer-list nil
45 "List of values to restore into `tags-table-list-pointer' when it hits nil.")
46
47(defvar tags-table-set-list nil
48 "List of sets of tags table which have been used together in the past.
49Each element is a list of strings which are file names.")
50
51;;;###autoload
52(defvar find-tag-hook nil
53 "*Hook to be run by \\[find-tag] after finding a tag. See `run-hooks'.
54The value in the buffer in which \\[find-tag] is done is used,
55not the value in the buffer \\[find-tag] goes to.")
56
57;;;###autoload
58(defvar find-tag-default-function nil
59 "*If non-nil, a function of no arguments used by \\[find-tag] to pick a
60default tag. If nil, and the symbol that is the value of `major-mode'
61has a `find-tag-default-function' property (see `put'), that is used.
62Otherwise, `find-tag-default' is used.")
63
64;;;###autoload
65(defvar default-tags-table-function nil
66 "*If non-nil, a function of no arguments to choose a default tags file
67for a particular buffer.")
68\f
69;; Tags table state.
70;; These variables are local in tags table buffers.
ff1f0fa6 71
9708f7fc
RM
72(defvar tag-lines-already-matched nil
73 "List of positions of beginnings of lines within the tags table
74that are already matched.")
ff1f0fa6 75
9708f7fc
RM
76(defvar tags-table-files nil
77 "List of file names covered by current tags table.
78nil means it has not yet been computed; use `tags-table-files' to do so.")
79
80(defvar tags-completion-table nil
81 "Alist of tag names defined in current tags table.")
82
83(defvar tags-included-tables nil
84 "List of tags tables included by the current tags table.")
85
86(defvar next-file-list nil
87 "List of files for \\[next-file] to process.")
88\f
89;; Hooks for file formats.
90
91(defvar tags-table-format-hooks '(etags-recognize-tags-table
9ef8b0d6 92 recognize-empty-tags-table)
9708f7fc
RM
93 "List of functions to be called in a tags table buffer to identify
94the type of tags table. The functions are called in order, with no arguments,
95until one returns non-nil. The function should make buffer-local bindings
96of the format-parsing tags function variables if successful.")
97
98(defvar file-of-tag-function nil
99 "Function to do the work of `file-of-tag' (which see).")
100(defvar tags-table-files-function nil
101 "Function to do the work of `tags-table-files' (which see).")
102(defvar tags-completion-table-function nil
103 "Function to build the tags-completion-table.")
104(defvar snarf-tag-function nil
105 "Function to get info about a matched tag for `goto-tag-location-function'.")
106(defvar goto-tag-location-function nil
107 "Function of to go to the location in the buffer specified by a tag.
108One argument, the tag info returned by `snarf-tag-function'.")
109(defvar find-tag-regexp-search-function nil
110 "Search function passed to `find-tag-in-order' for finding a regexp tag.")
111(defvar find-tag-regexp-tag-order nil
112 "Tag order passed to `find-tag-in-order' for finding a regexp tag.")
113(defvar find-tag-regexp-next-line-after-failure-p nil
114 "Flag passed to `find-tag-in-order' for finding a regexp tag.")
115(defvar find-tag-search-function nil
116 "Search function passed to `find-tag-in-order' for finding a tag.")
117(defvar find-tag-tag-order nil
118 "Tag order passed to `find-tag-in-order' for finding a tag.")
119(defvar find-tag-next-line-after-failure-p nil
120 "Flag passed to `find-tag-in-order' for finding a tag.")
121(defvar list-tags-function nil
122 "Function to do the work of `list-tags' (which see).")
123(defvar tags-apropos-function nil
124 "Function to do the work of `tags-apropos' (which see).")
125(defvar tags-included-tables-function nil
126 "Function to do the work of `tags-included-tables' (which see).")
127(defvar verify-tags-table-function nil
128 "Function to return t iff the current buffer vontains a valid
129\(already initialized\) tags file.")
130\f
131(defun initialize-new-tags-table ()
132 "Initialize the tags table in the current buffer.
133Returns non-nil iff it is a valid tags table."
134 (make-local-variable 'tag-lines-already-matched)
135 (make-local-variable 'tags-table-files)
136 (make-local-variable 'tags-completion-table)
137 (make-local-variable 'tags-included-tables)
138 (setq tags-table-files nil
139 tag-lines-already-matched nil
140 tags-completion-table nil
141 tags-included-tables nil)
142 ;; Value is t if we have found a valid tags table buffer.
143 (let ((hooks tags-table-format-hooks))
144 (while (and hooks
145 (not (funcall (car hooks))))
146 (setq hooks (cdr hooks)))
147 hooks))
ff1f0fa6 148
c086701a 149;;;###autoload
9708f7fc
RM
150(defun visit-tags-table (file &optional local)
151 "Tell tags commands to use tags table file FILE.
ff1f0fa6 152FILE should be the name of a file created with the `etags' program.
9708f7fc
RM
153A directory name is ok too; it means file TAGS in that directory.
154
155Normally \\[visit-tags-table] sets the global value of `tags-file-name'.
156With a prefix arg, set the buffer-local value instead.
157When you find a tag with \\[find-tag], the buffer it finds the tag
158in is given a local value of this variable which is the name of the tags
159file the tag was in."
ff1f0fa6
JB
160 (interactive (list (read-file-name "Visit tags table: (default TAGS) "
161 default-directory
9708f7fc
RM
162 (expand-file-name "TAGS"
163 default-directory)
164 t)
165 current-prefix-arg))
ff1f0fa6 166 (if (file-directory-p file)
9708f7fc
RM
167 (setq file (expand-file-name "TAGS" file)))
168 (if local
169 (setq tags-file-name file)
170 (kill-local-variable 'tags-file-name)
171 (setq-default tags-file-name file))
172 (save-excursion
173 (visit-tags-file t)))
174
175(defun visit-tags-table-buffer (&optional cont)
176 "Select the buffer containing the current tags table.
177If optional arg is t, visit the next table in `tags-table-list'.
178If optional arg is the atom `reset', reset to the head of `tags-table-list'.
179If optional arg is the atom `same', don't look for a new table;
180 just select the buffer.
181If arg is nil or absent, choose a buffer from information in
182`tags-file-name', `tags-table-list', `tags-table-list-pointer'.
183Returns t if it visits a tags table, or nil if there are no more in the list."
184 (if (eq cont 'same)
185 (let ((tags-file-name (car tags-table-list-pointer)))
186 (if (null tags-file-name)
187 nil
188 (visit-tags-file nil)
189 t))
190 (let ((put-in-list t))
191 (if (cond ((eq cont 'reset)
192 (setq tags-table-list-pointer tags-table-list
193 cont nil)
194 nil)
195 (cont
196 (setq tags-table-list-pointer (cdr tags-table-list-pointer))
197 (if (tags-included-tables)
198 (progn
199 ;; Move into the included tags tables.
200 (if tags-table-list-pointer
201 (setq tags-table-parent-pointer-list
202 (cons tags-table-list-pointer
203 tags-table-parent-pointer-list)))
204 (setq tags-table-list-pointer tags-included-tables)))
205 (or tags-table-list-pointer
206 ;; Pop back to the tags table after the one which includes
207 ;; this one.
208 (setq tags-table-list-pointer
209 (car tags-table-parent-pointer-list)
210 tags-table-parent-pointer-list
211 (cdr tags-table-parent-pointer-list)))
212 (setq put-in-list nil)
213 (null tags-table-list-pointer)))
214 ;; No more tags table files in the list.
215 nil
216 (setq tags-file-name
217 (or (if cont
218 (and tags-table-list-pointer
219 (or (car tags-table-list-pointer)
220 ;; nil means look for TAGS in current directory.
221 (if (file-exists-p
222 (expand-file-name "TAGS"
223 default-directory))
224 (expand-file-name "TAGS"
225 default-directory))))
226 (cdr (assq 'tags-file-name (buffer-local-variables))))
227 (and default-tags-table-function
228 (funcall default-tags-table-function))
21800cb8
RM
229 ;; Look for a tags table that contains
230 ;; tags for the current buffer's file.
231 (let ((tables tags-table-list)
232 (this-file (buffer-file-name))
233 (found nil))
234 (save-excursion
235 (while tables
236 (if (assoc this-file
237 (let ((tags-file-name (car tables)))
238 (visit-tags-file nil)
239 (or tags-table-files
240 (setq tags-table-files
241 (funcall
242 tags-table-files-function)))))
243 (setq found (car tables)
244 tables nil)
245 (setq tables (cdr tables)))))
246 found)
9708f7fc
RM
247 (car tags-table-list-pointer)
248 tags-file-name
249 (expand-file-name
250 (read-file-name "Visit tags table: (default TAGS) "
251 default-directory
252 (expand-file-name "TAGS" default-directory)
253 t))))
9708f7fc
RM
254 (visit-tags-file put-in-list)
255 t))))
256
257;; Visit tags-file-name and check that it's a valid tags table.
258;; On return, tags-table-list and tags-table-list-pointer
259;; point to tags-file-name.
260(defun visit-tags-file (put-in-list)
261 ;; FILE is never changed, but we don't just use tags-file-name
262 ;; directly because we don't want to get its buffer-local value
263 ;; in the buffer we switch to.
264 (let ((file tags-file-name))
9508896e
JB
265 (if (file-directory-p file)
266 (setq file (expand-file-name "TAGS" file)))
9708f7fc
RM
267 (if (if (get-file-buffer file)
268 (let (win)
269 (set-buffer (get-file-buffer file))
270 (setq win (or verify-tags-table-function
271 (initialize-new-tags-table)))
272 (if (or (verify-visited-file-modtime (current-buffer))
273 (not (yes-or-no-p
274 "Tags file has changed, read new contents? ")))
275 (and win (funcall verify-tags-table-function))
276 (revert-buffer t t)
277 (initialize-new-tags-table)))
278 (set-buffer (find-file-noselect file))
9ef8b0d6
RM
279 (or (string= file buffer-file-name)
280 ;; find-file-noselect has changed the file name.
281 ;; Propagate the change to tags-file-name and tags-table-list.
282 (let ((tail (assoc file tags-table-list)))
283 (if tail
284 (setcar tail buffer-file-name))
285 (setq tags-file-name buffer-file-name)))
9708f7fc
RM
286 (initialize-new-tags-table))
287
288 (if (and put-in-list
289 (not (equal file (car tags-table-list-pointer))))
290 (let (elt)
291 ;; Bury the tags table buffer so it
292 ;; doesn't get in the user's way.
293 (bury-buffer (current-buffer))
294 ;; Look for this file in the current list of tags files.
295 (if (setq elt (member file tags-table-list))
296 (if (eq elt tags-table-list)
297 ;; Already at the head of the list.
298 ()
299 ;; Rotate this element to the head of the search list.
300 (setq tags-table-list-pointer (nconc elt tags-table-list))
301 (while (not (eq (cdr tags-table-list) elt))
302 (setq tags-table-list (cdr tags-table-list)))
303 (setcdr tags-table-list nil)
304 (setq tags-table-list tags-table-list-pointer))
305 ;; The table is not in the current set.
306 ;; Try to find it in another previously used set.
307 (let ((sets tags-table-set-list))
308 (while (and sets
309 (not (setq elt (member file
310 (car sets)))))
311 (setq sets (cdr sets)))
312 (if sets
313 (progn
314 ;; Found in some other set. Switch to that set, making
315 ;; the selected tags table the head of the search list.
316 (or (memq tags-table-list tags-table-set-list)
317 ;; Save the current list.
318 (setq tags-table-set-list
319 (cons tags-table-list tags-table-set-list)))
320 (setq tags-table-list (car sets))
321 (if (eq elt tags-table-list)
322 ;; Already at the head of the list
323 ()
324 ;; Rotate this element to the head of the list.
325 (setq tags-table-list-pointer
326 (nconc elt tags-table-list))
327 (while (not (eq (cdr tags-table-list) elt))
328 (setq tags-table-list (cdr tags-table-list)))
329 (setcdr tags-table-list nil)
330 (setq tags-table-list tags-table-list-pointer)
331 (setcar sets tags-table-list)))
332 ;; Not found in any current set.
333 (if (and tags-table-list
334 (y-or-n-p
335 (concat "Add " file
336 " to current list of tags tables? ")))
337 ;; Add it to the current list.
338 (setq tags-table-list
339 (cons file tags-table-list))
340 ;; Make a fresh list, and store the old one.
341 (or (memq tags-table-list tags-table-set-list)
342 (setq tags-table-set-list
343 (cons tags-table-list tags-table-set-list)))
344 (setq tags-table-list (cons file nil)))
345 (setq tags-table-list-pointer tags-table-list))))))
346
347 ;; The buffer was not valid. Don't use it again.
348 (kill-local-variable 'tags-file-name)
349 (setq tags-file-name nil)
350 (error "File %s is not a valid tags table" buffer-file-name))))
ff1f0fa6
JB
351
352(defun file-of-tag ()
353 "Return the file name of the file whose tags point is within.
9708f7fc
RM
354Assumes the tags table is the current buffer.
355File name returned is relative to tags table file's directory."
356 (funcall file-of-tag-function))
ff1f0fa6 357
c086701a 358;;;###autoload
9708f7fc
RM
359(defun tags-table-files ()
360 "Return a list of files in the current tags table.
ff1f0fa6
JB
361File names returned are absolute."
362 (save-excursion
9708f7fc
RM
363 (visit-tags-table-buffer)
364 (or tags-table-files
365 (setq tags-table-files
366 (funcall tags-table-files-function)))))
367
368(defun tags-included-tables ()
369 "Return a list of tags tables included by the current table."
370 (or tags-included-tables
371 (setq tags-included-tables (funcall tags-included-tables-function))))
372\f
373;; Build tags-completion-table on demand. The single current tags table
374;; and its included tags tables (and their included tables, etc.) have
375;; their tags included in the completion table.
376(defun tags-completion-table ()
377 (or tags-completion-table
378 (condition-case ()
379 (prog2
380 (message "Making tags completion table for %s..." buffer-file-name)
381 (let ((included (tags-included-tables))
382 (table (funcall tags-completion-table-function)))
383 (save-excursion
384 (while included
385 (let ((tags-file-name (car included)))
386 (visit-tags-file nil))
387 (if (tags-completion-table)
388 (mapatoms (function
389 (lambda (sym)
390 (intern (symbol-name sym) table)))
391 tags-completion-table))
392 (setq included (cdr included))))
393 (setq tags-completion-table table))
394 (message "Making tags completion table for %s...done"
395 buffer-file-name))
396 (quit (message "Tags completion table construction aborted.")
397 (setq tags-completion-table nil)))))
398
399;; Completion function for tags. Does normal try-completion,
400;; but builds tags-completion-table on demand.
401(defun tags-complete-tag (string predicate what)
402 (save-excursion
403 (visit-tags-table-buffer)
404 (if (eq what t)
405 (all-completions string (tags-completion-table) predicate)
406 (try-completion string (tags-completion-table) predicate))))
ff1f0fa6
JB
407
408;; Return a default tag to search for, based on the text at point.
409(defun find-tag-default ()
410 (save-excursion
411 (while (looking-at "\\sw\\|\\s_")
412 (forward-char 1))
9708f7fc
RM
413 (if (or (re-search-backward "\\sw\\|\\s_"
414 (save-excursion (beginning-of-line) (point))
415 t)
416 (re-search-forward "\\(\\sw\\|\\s_\\)+"
417 (save-excursion (end-of-line) (point))
418 t))
419 (progn (goto-char (match-end 0))
ff1f0fa6
JB
420 (buffer-substring (point)
421 (progn (forward-sexp -1)
422 (while (looking-at "\\s'")
423 (forward-char 1))
424 (point))))
425 nil)))
426
9708f7fc 427;; Read a tag name from the minibuffer with defaulting and completion.
ff1f0fa6 428(defun find-tag-tag (string)
9708f7fc
RM
429 (let* ((default (funcall (or find-tag-default-function
430 (get major-mode 'find-tag-default-function)
431 'find-tag-default)))
432 (spec (completing-read (if default
433 (format "%s(default %s) " string default)
434 string)
435 'tags-complete-tag)))
ff1f0fa6 436 (list (if (equal spec "")
9708f7fc 437 (or default (error "There is no default tag"))
ff1f0fa6
JB
438 spec))))
439
21800cb8
RM
440(defvar last-tag nil
441 "Last tag found by \\[find-tag].")
442
c086701a 443;;;###autoload
9708f7fc
RM
444(defun find-tag-noselect (tagname &optional next-p regexp-p)
445 "Find tag (in current tags table) whose name contains TAGNAME.
446Returns the buffer containing the tag's definition moves its point there,
447but does not select the buffer.
448The default for TAGNAME is the expression in the buffer near point.
449
450If second arg NEXT-P is non-nil (interactively, with prefix arg), search
451for another tag that matches the last tagname or regexp used. When there
452are multiple matches for a tag, more exact matches are found first.
ff1f0fa6 453
9708f7fc
RM
454If third arg REGEXP-P is non-nil, treat TAGNAME as a regexp.
455
456See documentation of variable `tags-file-name'."
ff1f0fa6
JB
457 (interactive (if current-prefix-arg
458 '(nil t)
459 (find-tag-tag "Find tag: ")))
9708f7fc 460 (let ((local-find-tag-hook find-tag-hook))
21800cb8 461 (if next-p
ab13123a 462 (visit-tags-table-buffer 'same)
21800cb8
RM
463 (setq last-tag tagname)
464 (visit-tags-table-buffer 'reset))
465 (prog1
466 (find-tag-in-order (if next-p last-tag tagname)
467 (if regexp-p
468 find-tag-regexp-search-function
469 find-tag-search-function)
470 (if regexp-p
471 find-tag-regexp-tag-order
472 find-tag-tag-order)
473 (if regexp-p
474 find-tag-regexp-next-line-after-failure-p
475 find-tag-next-line-after-failure-p)
476 (if regexp-p "matching" "containing")
477 (not next-p))
478 (run-hooks 'local-find-tag-hook))))
ff1f0fa6 479
c086701a 480;;;###autoload
9708f7fc
RM
481(defun find-tag (tagname &optional next-p)
482 "Find tag (in current tags table) whose name contains TAGNAME.
483Select the buffer containing the tag's definition, and move point there.
484The default for TAGNAME is the expression in the buffer around or before point.
c086701a 485
9708f7fc
RM
486If second arg NEXT-P is non-nil (interactively, with prefix arg), search
487for another tag that matches the last tagname used. When there are
488multiple matches, more exact matches are found first.
ff1f0fa6 489
9708f7fc 490See documentation of variable `tags-file-name'."
ff1f0fa6
JB
491 (interactive (if current-prefix-arg
492 '(nil t)
6218e8c6 493 (find-tag-tag "Find tag: ")))
9708f7fc
RM
494 (switch-to-buffer (find-tag-noselect tagname next-p)))
495;;;###autoload (define-key esc-map "." 'find-tag)
ff1f0fa6 496
daa37602 497;;;###autoload
9708f7fc
RM
498(defun find-tag-other-window (tagname &optional next-p)
499 "Find tag (in current tags table) whose name contains TAGNAME.
500Select the buffer containing the tag's definition
501in another window, and move point there.
502The default for TAGNAME is the expression in the buffer around or before point.
503
504If second arg NEXT-P is non-nil (interactively, with prefix arg), search
505for another tag that matches the last tagname used. When there are
506multiple matches, more exact matches are found first.
507
508See documentation of variable `tags-file-name'."
509 (interactive (if current-prefix-arg
510 '(nil t)
511 (find-tag-tag "Find tag other window: ")))
512 (switch-to-buffer-other-window (find-tag-noselect tagname next-p)))
513;;;###autoload (define-key ctl-x-4-map "." 'find-tag-other-window)
514
29add8b9 515;;;###autoload
9708f7fc 516(defun find-tag-other-frame (tagname &optional next-p)
daa37602
JB
517 "Find tag (in current tag table) whose name contains TAGNAME.
518 Selects the buffer that the tag is contained in in another frame
519and puts point at its definition.
520 If TAGNAME is a null string, the expression in the buffer
521around or before point is used as the tag name.
9708f7fc 522 If second arg NEXT-P is non-nil (interactively, with prefix arg),
daa37602
JB
523searches for the next tag in the tag table
524that matches the tagname used in the previous find-tag.
525
9708f7fc 526See documentation of variable `tags-file-name'."
daa37602
JB
527 (interactive (if current-prefix-arg
528 '(nil t)
529 (find-tag-tag "Find tag other window: ")))
9708f7fc
RM
530 (let ((pop-up-frames t))
531 (find-tag-other-window tagname next-p)))
532;;;###autoload (define-key ctl-x-5-map "." 'find-tag-other-frame)
533
daa37602 534;;;###autoload
9708f7fc
RM
535(defun find-tag-regexp (regexp &optional next-p other-window)
536 "Find tag (in current tags table) whose name matches REGEXP.
537Select the buffer containing the tag's definition and move point there.
daa37602 538
9708f7fc
RM
539If second arg NEXT-P is non-nil (interactively, with prefix arg), search
540for another tag that matches the last tagname used.
541
542If third arg OTHER-WINDOW is non-nil, select the buffer in another window.
543
544See documentation of variable `tags-file-name'."
545 (interactive (if current-prefix-arg
546 '(nil t)
547 (read-string "Find tag regexp: ")))
548 (funcall (if other-window 'switch-to-buffer-other-window 'switch-to-buffer)
549 (find-tag-noselect regexp next-p t)))
550\f
551;; Internal tag finding function.
552
553;; PATTERN is a string to pass to second arg SEARCH-FORWARD-FUNC, and to
554;; any member of the function list ORDER (third arg). If ORDER is nil,
555;; use saved state to continue a previous search.
556
557;; Fourth arg MATCHING is a string, an English '-ing' word, to be used in
558;; an error message.
559
560;; Fifth arg NEXT-LINE-AFTER-FAILURE-P is non-nil if after a failed match,
561;; point should be moved to the next line.
562
563;; Algorithm is as follows. For each qualifier-func in ORDER, go to
564;; beginning of tags file, and perform inner loop: for each naive match for
565;; PATTERN found using SEARCH-FORWARD-FUNC, qualify the naive match using
566;; qualifier-func. If it qualifies, go to the specified line in the
567;; specified source file and return. Qualified matches are remembered to
568;; avoid repetition. State is saved so that the loop can be continued.
569
570(defun find-tag-in-order (pattern search-forward-func order
571 next-line-after-failure-p matching
572 first-search)
573 (let (file ;name of file containing tag
574 tag-info ;where to find the tag in FILE
575 tags-table-file ;name of tags file
576 (first-table t)
577 (tag-order order)
578 goto-func
579 )
580 (save-excursion
581 (or first-search
582 (visit-tags-table-buffer))
583 ;; Get a qualified match.
584 (catch 'qualified-match-found
585 (while (or first-table
586 (visit-tags-table-buffer t))
587
588 (if first-search
589 (setq tag-lines-already-matched nil))
590
6218e8c6
RM
591 (and first-search first-table
592 ;; Start at beginning of tags file.
593 (goto-char (point-min)))
594 (setq first-table nil)
9708f7fc
RM
595
596 (setq tags-table-file buffer-file-name)
597 (while order
598 (while (funcall search-forward-func pattern nil t)
599 ;; Naive match found. Qualify the match.
600 (and (funcall (car order) pattern)
601 ;; Make sure it is not a previous qualified match.
602 ;; Use of `memq' depends on numbers being eq.
603 (not (memq (save-excursion (beginning-of-line) (point))
604 tag-lines-already-matched))
605 (throw 'qualified-match-found nil))
606 (if next-line-after-failure-p
607 (forward-line 1)))
608 ;; Try the next flavor of match.
609 (setq order (cdr order))
610 (goto-char (point-min)))
611 (setq order tag-order))
612 ;; We throw out on match, so only get here if there were no matches.
613 (error "No %stags %s %s" (if first-search "" "more ")
614 matching pattern))
615
616 ;; Found a tag; extract location info.
617 (beginning-of-line)
618 (setq tag-lines-already-matched (cons (point)
619 tag-lines-already-matched))
620 ;; Expand the filename, using the tags table buffer's default-directory.
621 (setq file (expand-file-name (file-of-tag))
622 tag-info (funcall snarf-tag-function))
623
624 ;; Get the local value in the tags table buffer.
625 (setq goto-func goto-tag-location-function)
626
627 ;; Find the right line in the specified file.
628 (set-buffer (find-file-noselect file))
629 (widen)
630 (push-mark)
631 (funcall goto-func tag-info)
632
633 ;; Give this buffer a local value of tags-file-name.
634 ;; The next time visit-tags-table-buffer is called,
635 ;; it will use the same tags table that found a match in this buffer.
636 (make-local-variable 'tags-file-name)
637 (setq tags-file-name tags-table-file)
638
639 ;; Return the buffer where the tag was found.
640 (current-buffer))))
641\f
642;; `etags' TAGS file format support.
643
644(defun etags-recognize-tags-table ()
645 (and (eq (char-after 1) ?\f)
9508896e
JB
646 ;; It is annoying to flash messages on the screen briefly,
647 ;; and this message is not useful. -- rms
648 ;; (message "%s is an `etags' TAGS file" buffer-file-name)
9708f7fc
RM
649 (mapcar (function (lambda (elt)
650 (make-local-variable (car elt))
651 (set (car elt) (cdr elt))))
652 '((file-of-tag-function . etags-file-of-tag)
653 (tags-table-files-function . etags-tags-table-files)
654 (tags-completion-table-function . etags-tags-completion-table)
655 (snarf-tag-function . etags-snarf-tag)
656 (goto-tag-location-function . etags-goto-tag-location)
657 (find-tag-regexp-search-function . re-search-forward)
658 (find-tag-regexp-tag-order . (tag-re-match-p))
659 (find-tag-regexp-next-line-after-failuire-p . t)
660 (find-tag-search-function . search-forward)
661 (find-tag-tag-order . (tag-exact-match-p tag-word-match-p
662 tag-any-match-p))
663 (find-tag-next-line-after-failure-p . nil)
664 (list-tags-function . etags-list-tags)
665 (tags-apropos-function . etags-tags-apropos)
666 (tags-included-tables-function . etags-tags-included-tables)
667 (verify-tags-table-function . etags-verify-tags-table)
668 ))))
669
670(defun etags-verify-tags-table ()
671 (= (char-after 1) ?\f))
672
673(defun etags-file-of-tag ()
674 (save-excursion
675 (search-backward "\f\n")
676 (forward-char 2)
677 (buffer-substring (point)
678 (progn (skip-chars-forward "^,") (point)))))
679
680(defun etags-tags-completion-table ()
681 (let ((table (make-vector 511 0)))
682 (save-excursion
683 (goto-char (point-min))
684 (while (search-forward "\177" nil t)
685 ;; Handle multiple \177's on a line.
686 (save-excursion
687 (skip-chars-backward "^-A-Za-z0-9_$\n") ;sym syntax? XXX
688 (or (bolp)
689 (intern (buffer-substring
690 (point)
691 (progn
692 (skip-chars-backward "-A-Za-z0-9_$")
693 ;; ??? New
694 ;; `::' in the middle of a C++ tag.
695 (and (= (preceding-char) ?:)
696 (= (char-after (- (point) 2)) ?:)
697 (progn
698 (backward-char 2)
699 (skip-chars-backward
700 "-A-Za-z0-9_$")))
701 (point)))
702 table)))))
703 table))
704
705(defun etags-snarf-tag ()
706 (let (tag-text startpos)
707 (search-forward "\177")
708 (setq tag-text (buffer-substring (1- (point))
709 (save-excursion (beginning-of-line)
710 (point))))
711 (search-forward ",")
712 (setq startpos (string-to-int (buffer-substring
713 (point)
714 (progn (skip-chars-forward "0-9")
715 (point)))))
716 ;; Leave point on the next line of the tags file.
717 (forward-line 1)
718 (cons tag-text startpos)))
719
720(defun etags-goto-tag-location (tag-info)
721 (let ((startpos (cdr tag-info))
722 ;; This constant is 1/2 the initial search window.
723 ;; There is no sense in making it too small,
724 ;; since just going around the loop once probably
725 ;; costs about as much as searching 2000 chars.
726 (offset 1000)
727 (found nil)
728 (pat (concat "^" (regexp-quote (car tag-info)))))
729 (or startpos
730 (setq startpos (point-min)))
731 (while (and (not found)
732 (progn
733 (goto-char (- startpos offset))
734 (not (bobp))))
735 (setq found
736 (re-search-forward pat (+ startpos offset) t)
737 offset (* 3 offset))) ; expand search window
738 (or found
739 (re-search-forward pat nil t)
740 (error "`%s' not found in %s; time to rerun etags"
741 pat buffer-file-name)))
742 (beginning-of-line))
743
744(defun etags-list-tags (file)
745 (goto-char 1)
746 (if (not (search-forward (concat "\f\n" file ",") nil t))
747 nil
748 (forward-line 1)
749 (while (not (or (eobp) (looking-at "\f")))
750 (princ (buffer-substring (point)
751 (progn (skip-chars-forward "^\177")
752 (point))))
753 (terpri)
754 (forward-line 1))))
755
756(defun etags-tags-apropos (string)
757 (goto-char 1)
758 (while (re-search-forward string nil t)
759 (beginning-of-line)
760 (princ (buffer-substring (point)
761 (progn (skip-chars-forward "^\177")
762 (point))))
763 (terpri)
764 (forward-line 1)))
765
766(defun etags-tags-table-files ()
767 (let ((files nil)
768 beg)
769 (goto-char (point-min))
770 (while (search-forward "\f\n" nil t)
771 (setq beg (point))
772 (skip-chars-forward "^,\n")
773 (or (looking-at ",include$")
774 ;; Expand in the default-directory of the tags table buffer.
775 (setq files (cons (expand-file-name (buffer-substring beg (point)))
776 files))))
777 (nreverse files)))
778
779(defun etags-tags-included-tables ()
780 (let ((files nil)
781 beg)
782 (goto-char (point-min))
783 (while (search-forward "\f\n" nil t)
784 (setq beg (point))
785 (skip-chars-forward "^,\n")
786 (if (looking-at ",include$")
787 ;; Expand in the default-directory of the tags table buffer.
788 (setq files (cons (expand-file-name (buffer-substring beg (point)))
789 files))))
790 (nreverse files)))
791\f
792;; Empty tags file support.
793
794(defun recognize-empty-tags-table ()
795 (and (zerop (buffer-size))
796 (mapcar (function (lambda (sym)
797 (make-local-variable sym)
9a50b93f 798 (set sym 'ignore)))
9708f7fc
RM
799 '(tags-table-files-function
800 tags-completion-table-function
801 find-tag-regexp-search-function
802 find-tag-search-function
803 tags-apropos-function
804 tags-included-tables-function))
805 (set (make-local-variable 'verify-tags-table-function)
806 (function (lambda ()
807 (zerop (buffer-size)))))))
808\f
809;;; Match qualifier functions for tagnames.
810
6218e8c6
RM
811;; This might be a neat idea, but it's too hairy at the moment.
812;;(defmacro tags-with-syntax (&rest body)
813;; (` (let ((current (current-buffer))
814;; (otable (syntax-table))
815;; (buffer (find-file-noselect (file-of-tag)))
816;; table)
817;; (unwind-protect
818;; (progn
819;; (set-buffer buffer)
820;; (setq table (syntax-table))
821;; (set-buffer current)
822;; (set-syntax-table table)
823;; (,@ body))
824;; (set-syntax-table otable)))))
825;;(put 'tags-with-syntax 'edebug-form-spec '(&rest form))
8a4c10dc 826
9708f7fc
RM
827;; t if point is at a tag line that matches TAG "exactly".
828;; point should be just after a string that matches TAG.
6218e8c6
RM
829(defun tag-exact-match-p (tag)
830 (and (looking-at "\\Sw.*\177") (looking-at "\\S_.*\177") ;not a symbol char
831 (save-excursion
832 (backward-char (1+ (length tag)))
833 (and (looking-at "\\Sw") (looking-at "\\S_")))))
9708f7fc
RM
834
835;; t if point is at a tag line that matches TAG as a word.
836;; point should be just after a string that matches TAG.
837(defun tag-word-match-p (tag)
838 (and (looking-at "\\b.*\177")
839 (save-excursion (backward-char (1+ (length tag)))
840 (looking-at "\\b"))))
841
842;; t if point is in a tag line with a tag containing TAG as a substring.
843(defun tag-any-match-p (tag)
844 (looking-at ".*\177"))
ff1f0fa6 845
9708f7fc
RM
846;; t if point is at a tag line that matches RE as a regexp.
847(defun tag-re-match-p (re)
848 (save-excursion
849 (beginning-of-line)
850 (let ((bol (point)))
851 (and (search-forward "\177" (save-excursion (end-of-line) (point)) t)
852 (re-search-backward re bol t)))))
853\f
c086701a 854;;;###autoload
9708f7fc
RM
855(defun next-file (&optional initialize novisit)
856 "Select next file among files in current tags table.
857Non-nil first argument (prefix arg, if interactive)
858initializes to the beginning of the list of files in the tags table.
859
860Non-nil second argument NOVISIT means use a temporary buffer
861 to save time and avoid uninteresting warnings.
862
863Value is nil if the file was already visited;
864if the file was newly read in, the value is the filename."
ff1f0fa6 865 (interactive "P")
9708f7fc
RM
866 (and initialize
867 (save-excursion
868 (visit-tags-table-buffer 'reset)
869 (setq next-file-list (tags-table-files))))
ff1f0fa6 870 (or next-file-list
9708f7fc
RM
871 (save-excursion
872 ;; When doing (visit-tag-table-buffer t),
873 ;; the tags table buffer must be current.
874 (if (and (visit-tags-table-buffer 'same)
875 (visit-tags-table-buffer t))
876 (setq next-file-list (tags-table-files))
877 (and novisit
878 (get-buffer " *next-file*")
879 (kill-buffer " *next-file*"))
880 (error "All files processed."))))
881 (let ((new (not (get-file-buffer (car next-file-list)))))
882 (if (not (and new novisit))
883 (set-buffer (find-file-noselect (car next-file-list) novisit))
884 ;; Like find-file, but avoids random warning messages.
885 (set-buffer (get-buffer-create " *next-file*"))
886 (kill-all-local-variables)
887 (erase-buffer)
888 (setq new (car next-file-list))
889 (insert-file-contents new nil))
890 (setq next-file-list (cdr next-file-list))
891 new))
ff1f0fa6 892
9708f7fc
RM
893(defvar tags-loop-operate nil
894 "Form for `tags-loop-continue' to eval to change one file.")
895
896(defvar tags-loop-scan nil
897 "Form for `tags-loop-continue' to eval to scan one file.
898If it returns non-nil, this file needs processing by evalling
899\`tags-loop-operate'. Otherwise, move on to the next file.")
ff1f0fa6 900
c086701a 901;;;###autoload
ff1f0fa6
JB
902(defun tags-loop-continue (&optional first-time)
903 "Continue last \\[tags-search] or \\[tags-query-replace] command.
9708f7fc
RM
904Used noninteractively with non-nil argument to begin such a command.
905Two variables control the processing we do on each file:
906the value of `tags-loop-scan' is a form to be executed on each file
907to see if it is interesting (it returns non-nil if so)
908and `tags-loop-operate' is a form to execute to operate on an interesting file
909If the latter returns non-nil, we exit; otherwise we scan the next file."
ff1f0fa6 910 (interactive)
9708f7fc
RM
911 (let (new
912 (messaged nil))
913 (while
914 (progn
915 ;; Scan files quickly for the first or next interesting one.
916 (while (or first-time
917 (save-restriction
918 (widen)
919 (not (eval tags-loop-scan))))
920 (setq new (next-file first-time t))
921 ;; If NEW is non-nil, we got a temp buffer,
922 ;; and NEW is the file name.
923 (if (or messaged
924 (and (not first-time)
925 (> baud-rate search-slow-speed)
926 (setq messaged t)))
927 (message "Scanning file %s..." (or new buffer-file-name)))
928 (setq first-time nil)
929 (goto-char (point-min)))
930
931 ;; If we visited it in a temp buffer, visit it now for real.
932 (if new
933 (let ((pos (point)))
934 (erase-buffer)
935 (set-buffer (find-file-noselect new))
936 (widen)
937 (goto-char pos)))
938
939 (switch-to-buffer (current-buffer))
940
941 ;; Now operate on the file.
942 ;; If value is non-nil, continue to scan the next file.
943 (eval tags-loop-operate)))
944 (and messaged
945 (null tags-loop-operate)
946 (message "Scanning file %s...found" buffer-file-name))))
947
948;;;###autoload (define-key esc-map "," 'tags-loop-continue)
ff1f0fa6 949
c086701a 950;;;###autoload
ff1f0fa6 951(defun tags-search (regexp)
9708f7fc 952 "Search through all files listed in tags table for match for REGEXP.
ff1f0fa6
JB
953Stops when a match is found.
954To continue searching for next match, use command \\[tags-loop-continue].
955
9708f7fc 956See documentation of variable `tags-file-name'."
ff1f0fa6
JB
957 (interactive "sTags search (regexp): ")
958 (if (and (equal regexp "")
9708f7fc
RM
959 (eq (car tags-loop-scan) 're-search-forward)
960 (eq tags-loop-operate t))
961 ;; Continue last tags-search as if by M-,.
ff1f0fa6 962 (tags-loop-continue nil)
9708f7fc
RM
963 (setq tags-loop-scan
964 (list 're-search-forward regexp nil t)
965 tags-loop-operate nil)
ff1f0fa6
JB
966 (tags-loop-continue t)))
967
c086701a 968;;;###autoload
ff1f0fa6 969(defun tags-query-replace (from to &optional delimited)
9708f7fc 970 "Query-replace-regexp FROM with TO through all files listed in tags table.
ff1f0fa6 971Third arg DELIMITED (prefix arg) means replace only word-delimited matches.
9708f7fc 972If you exit (\\[keyboard-quit] or ESC), you can resume the query-replace
ff1f0fa6
JB
973with the command \\[tags-loop-continue].
974
9708f7fc
RM
975See documentation of variable `tags-file-name'."
976 (interactive
977 "sTags query replace (regexp): \nsTags query replace %s by: \nP")
29add8b9
RM
978 (setq tags-loop-scan (list 'prog1
979 (list 'if (list 're-search-forward form nil t)
980 ;; When we find a match, move back
981 ;; to the beginning of it so perform-replace
982 ;; will see it.
983 '(goto-char (match-beginning 0))))
9708f7fc 984 tags-loop-operate (list 'perform-replace from to t t delimited))
ff1f0fa6 985 (tags-loop-continue t))
9708f7fc 986\f
c086701a 987;;;###autoload
9708f7fc 988(defun list-tags (file)
ff1f0fa6 989 "Display list of tags in file FILE.
9708f7fc
RM
990FILE should not contain a directory specification
991unless it has one in the tags table."
992 (interactive (list (completing-read "List tags in file: " nil
993 'tags-table-files t nil)))
ff1f0fa6
JB
994 (with-output-to-temp-buffer "*Tags List*"
995 (princ "Tags in file ")
9708f7fc 996 (princ file)
ff1f0fa6
JB
997 (terpri)
998 (save-excursion
9708f7fc
RM
999 (let ((first-time t)
1000 (gotany nil))
1001 (while (visit-tags-table-buffer (if first-time 'reset t))
1002 (if (funcall list-tags-function file)
1003 (setq gotany t)))
1004 (or gotany
1005 (error "File %s not in current tags tables"))))))
ff1f0fa6 1006
c086701a 1007;;;###autoload
9708f7fc
RM
1008(defun tags-apropos (regexp)
1009 "Display list of all tags in tags table REGEXP matches."
1010 (interactive "sTags apropos (regexp): ")
ff1f0fa6
JB
1011 (with-output-to-temp-buffer "*Tags List*"
1012 (princ "Tags matching regexp ")
9708f7fc 1013 (prin1 regexp)
ff1f0fa6
JB
1014 (terpri)
1015 (save-excursion
9708f7fc
RM
1016 (let ((first-time nil))
1017 (while (visit-tags-table-buffer (if first-time 'reset t))
1018 (funcall tags-apropos-function))))))
1019\f
1020;;; XXX Kludge interface.
aa228418 1021
9708f7fc 1022;; XXX If a file is in multiple tables, selection may get the wrong one.
29add8b9 1023;;;###autoload
9708f7fc
RM
1024(defun select-tags-table ()
1025 "Select a tags table file from a menu of those you have already used.
1026The list of tags tables to select from is stored in `tags-table-file-list';
1027see the doc of that variable if you want to add names to the list."
1028 (interactive)
1029 (pop-to-buffer "*Tags Table List*")
1030 (setq buffer-read-only nil)
1031 (erase-buffer)
1032 (setq selective-display t
1033 selective-display-ellipses nil)
1034 (let ((set-list tags-table-set-list)
1035 (desired-point nil))
1036 (if tags-table-list
1037 (progn
1038 (setq desired-point (point-marker))
1039 (princ tags-table-list (current-buffer))
1040 (insert "\C-m")
1041 (prin1 (car tags-table-list) (current-buffer)) ;invisible
1042 (insert "\n")))
1043 (while set-list
1044 (if (eq (car set-list) tags-table-list)
1045 ;; Already printed it.
1046 ()
1047 (princ (car set-list) (current-buffer))
1048 (insert "\C-m")
1049 (prin1 (car (car set-list)) (current-buffer)) ;invisible
1050 (insert "\n"))
1051 (setq set-list (cdr set-list)))
1052 (if tags-file-name
1053 (progn
1054 (or desired-point
1055 (setq desired-point (point-marker)))
1056 (insert tags-file-name "\C-m")
1057 (prin1 tags-file-name (current-buffer)) ;invisible
1058 (insert "\n")))
1059 (setq set-list (delete tags-file-name
1060 (apply 'nconc (cons tags-table-list
1061 (mapcar 'copy-sequence
1062 tags-table-set-list)))))
1063 (while set-list
1064 (insert (car set-list) "\C-m")
1065 (prin1 (car set-list) (current-buffer)) ;invisible
1066 (insert "\n")
1067 (setq set-list (delete (car set-list) set-list)))
1068 (goto-char 1)
1069 (insert-before-markers
1070 "Type `t' to select a tags table or set of tags tables:\n\n")
1071 (if desired-point
1072 (goto-char desired-point))
1073 (set-window-start (selected-window) 1 t))
1074 (set-buffer-modified-p nil)
1075 (setq buffer-read-only t
1076 mode-name "Select Tags Table")
1077 (let ((map (make-sparse-keymap)))
1078 (define-key map "t" 'select-tags-table-select)
1079 (define-key map " " 'next-line)
1080 (define-key map "\^?" 'previous-line)
1081 (define-key map "n" 'next-line)
1082 (define-key map "p" 'previous-line)
1083 (define-key map "q" 'select-tags-table-quit)
1084 (use-local-map map)))
1085
1086(defun select-tags-table-select ()
1087 "Select the tags table named on this line."
1088 (interactive)
1089 (search-forward "\C-m")
1090 (let ((name (read (current-buffer))))
1091 (visit-tags-table name)
1092 (select-tags-table-quit)
1093 (message "Tags table now %s" name)))
49116ac0 1094
9708f7fc
RM
1095(defun select-tags-table-quit ()
1096 "Kill the buffer and delete the selected window."
1097 (interactive)
1098 (kill-buffer (current-buffer))
1099 (or (one-window-p)
1100 (delete-window)))
1101\f
1102;;;###autoload
1103(defun complete-tag ()
1104 "Perform tags completion on the text around point.
1105Completes to the set of names listed in the current tags table.
1106The string to complete is chosen in the same way as the default
ab13123a 1107for \\[find-tag] (which see)."
9708f7fc 1108 (interactive)
ab13123a
RM
1109 (or tags-table-list
1110 tags-file-name
1111 (error (substitute-command-keys
1112 "No tags table loaded. Try \\[visit-tags-table].")))
9708f7fc
RM
1113 (let ((pattern (funcall (or find-tag-default-function
1114 (get major-mode 'find-tag-default-function)
1115 'find-tag-default)))
1116 beg
1117 completion)
1118 (or pattern
1119 (error "Nothing to complete"))
1120 (search-backward pattern)
1121 (setq beg (point))
1122 (forward-char (length pattern))
1123 (setq completion (try-completion pattern 'tags-complete-tag nil))
1124 (cond ((eq completion t))
1125 ((null completion)
1126 (message "Can't find completion for \"%s\"" pattern)
1127 (ding))
1128 ((not (string= pattern completion))
1129 (delete-region beg (point))
1130 (insert completion))
1131 (t
1132 (message "Making completion list...")
1133 (with-output-to-temp-buffer " *Completions*"
1134 (display-completion-list
1135 (all-completions pattern 'tags-complete-tag nil)))
1136 (message "Making completion list...%s" "done")))))
6218e8c6
RM
1137
1138;;;###autoload (define-key esc-map "\t" 'complete-tag)
9708f7fc
RM
1139\f
1140(provide 'etags)
e5167999
ER
1141
1142;;; etags.el ends here