*** empty log message ***
[bpt/emacs.git] / lisp / progmodes / etags.el
CommitLineData
c0274f38
ER
1;;; etags.el --- tags facility for Emacs.
2
daa37602 3;; Copyright (C) 1985, 1986, 1988, 1992 Free Software Foundation, Inc.
ff1f0fa6
JB
4
5;; This file is part of GNU Emacs.
6
7;; GNU Emacs is free software; you can redistribute it and/or modify
8;; it under the terms of the GNU General Public License as published by
daa37602 9;; the Free Software Foundation; either version 2, or (at your option)
ff1f0fa6
JB
10;; any later version.
11
12;; GNU Emacs is distributed in the hope that it will be useful,
13;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;; GNU General Public License for more details.
16
17;; You should have received a copy of the GNU General Public License
18;; along with GNU Emacs; see the file COPYING. If not, write to
19;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
20
21
c086701a
JB
22;;;###autoload
23(defvar tags-file-name nil "\
24*File name of tag table.
25To switch to a new tag table, setting this variable is sufficient.
26Use the `etags' program to make a tag table file.")
ff1f0fa6
JB
27
28(defvar tag-table-files nil
29 "List of file names covered by current tag table.
30nil means it has not been computed yet; do (tag-table-files) to compute it.")
31
32(defvar last-tag nil
33 "Tag found by the last find-tag.")
34
c086701a 35;;;###autoload
ff1f0fa6
JB
36(defun visit-tags-table (file)
37 "Tell tags commands to use tag table file FILE.
38FILE should be the name of a file created with the `etags' program.
39A directory name is ok too; it means file TAGS in that directory."
40 (interactive (list (read-file-name "Visit tags table: (default TAGS) "
41 default-directory
42 (concat default-directory "TAGS")
43 t)))
44 (setq file (expand-file-name file))
d54f9e30
JB
45 ;; Get rid of the prefixes added by the automounter.
46 (if (and (string-match "^/tmp_mnt/" file)
47 (file-exists-p (file-name-directory
48 (substring file (1- (match-end 0))))))
49 (setq file (substring file (1- (match-end 0)))))
ff1f0fa6
JB
50 (if (file-directory-p file)
51 (setq file (concat file "TAGS")))
52 (setq tag-table-files nil
53 tags-file-name file))
54
55(defun visit-tags-table-buffer ()
56 "Select the buffer containing the current tag table.
57This is a file whose name is in the variable tags-file-name."
58 (or tags-file-name
59 (call-interactively 'visit-tags-table))
60 (set-buffer (or (get-file-buffer tags-file-name)
61 (progn
62 (setq tag-table-files nil)
63 (find-file-noselect tags-file-name))))
64 (or (verify-visited-file-modtime (get-file-buffer tags-file-name))
65 (cond ((yes-or-no-p "Tags file has changed, read new contents? ")
66 (revert-buffer t t)
67 (setq tag-table-files nil))))
68 (or (eq (char-after 1) ?\^L)
69 (error "File %s not a valid tag table" tags-file-name)))
70
71(defun file-of-tag ()
72 "Return the file name of the file whose tags point is within.
73Assumes the tag table is the current buffer.
74File name returned is relative to tag table file's directory."
75 (let ((opoint (point))
76 prev size)
77 (save-excursion
78 (goto-char (point-min))
79 (while (< (point) opoint)
80 (forward-line 1)
81 (end-of-line)
82 (skip-chars-backward "^,\n")
83 (setq prev (point))
84 (setq size (read (current-buffer)))
85 (goto-char prev)
86 (forward-line 1)
87 (forward-char size))
88 (goto-char (1- prev))
89 (buffer-substring (point)
90 (progn (beginning-of-line) (point))))))
91
c086701a 92;;;###autoload
ff1f0fa6
JB
93(defun tag-table-files ()
94 "Return a list of files in the current tag table.
95File names returned are absolute."
96 (save-excursion
97 (visit-tags-table-buffer)
98 (or tag-table-files
99 (let (files)
100 (goto-char (point-min))
101 (while (not (eobp))
102 (forward-line 1)
103 (end-of-line)
104 (skip-chars-backward "^,\n")
105 (setq prev (point))
106 (setq size (read (current-buffer)))
107 (goto-char prev)
108 (setq files (cons (expand-file-name
109 (buffer-substring (1- (point))
110 (save-excursion
111 (beginning-of-line)
112 (point)))
113 (file-name-directory tags-file-name))
114 files))
115 (forward-line 1)
116 (forward-char size))
117 (setq tag-table-files (nreverse files))))))
118
119;; Return a default tag to search for, based on the text at point.
120(defun find-tag-default ()
121 (save-excursion
122 (while (looking-at "\\sw\\|\\s_")
123 (forward-char 1))
124 (if (re-search-backward "\\sw\\|\\s_" nil t)
125 (progn (forward-char 1)
126 (buffer-substring (point)
127 (progn (forward-sexp -1)
128 (while (looking-at "\\s'")
129 (forward-char 1))
130 (point))))
131 nil)))
132
133(defun find-tag-tag (string)
134 (let* ((default (find-tag-default))
135 (spec (read-string
136 (if default
137 (format "%s(default %s) " string default)
138 string))))
139 (list (if (equal spec "")
140 default
141 spec))))
142
aa228418
JB
143(defun tags-tag-match (tagname exact)
144 "Search for a match to the given tagname."
145 (if (not exact)
146 (search-forward tagname nil t)
147 (not (error-occurred
148 (while
149 (progn
150 (search-forward tagname)
151 (let ((before (char-syntax (char-after (1- (match-beginning 1)))))
152 (after (char-syntax (char-after (match-end 1)))))
153 (not (or (= before ?w) (= before ?_))
154 (= after ?w) (= after ?_)))
155 ))))
156 )
157 )
158
a71a736b
ER
159(defun find-tag-noselect (tagname exact &optional next)
160 "Find a tag and return its buffer, but don't select or display it."
aa228418
JB
161 (let (buffer file linebeg startpos (obuf (current-buffer)))
162 ;; save-excursion will do the wrong thing if the buffer containing the
163 ;; tag being searched for is current-buffer
164 (unwind-protect
165 (progn
166 (visit-tags-table-buffer)
167 (if (not next)
168 (goto-char (point-min))
169 (setq tagname last-tag))
170 (setq last-tag tagname)
171 (while (progn
172 (if (not (tags-tag-match tagname exact))
173 (error "No %sentries matching %s"
174 (if next "more " "") tagname))
175 (not (looking-at "[^\n\177]*\177"))))
176 (search-forward "\177")
177 (setq file (expand-file-name (file-of-tag)
178 (file-name-directory tags-file-name)))
179 (setq linebeg
180 (buffer-substring (1- (point))
181 (save-excursion (beginning-of-line) (point))))
182 (search-forward ",")
183 (setq startpos (read (current-buffer)))
184 (prog1
185 (set-buffer (find-file-noselect file))
186 (widen)
187 (push-mark)
188 (let ((offset 1000)
189 found
190 (pat (concat "^" (regexp-quote linebeg))))
191 (or startpos (setq startpos (point-min)))
192 (while (and (not found)
193 (progn
194 (goto-char (- startpos offset))
195 (not (bobp))))
196 (setq found
197 (re-search-forward pat (+ startpos offset) t))
198 (setq offset (* 3 offset)))
199 (or found
200 (re-search-forward pat nil t)
201 (error "%s not found in %s" pat file)))
202 (beginning-of-line)))
203 (set-buffer obuf))
a71a736b
ER
204 ))
205
c086701a 206;;;###autoload
ff1f0fa6
JB
207(defun find-tag (tagname &optional next other-window)
208 "Find tag (in current tag table) whose name contains TAGNAME.
209 Selects the buffer that the tag is contained in
210and puts point at its definition.
211 If TAGNAME is a null string, the expression in the buffer
212around or before point is used as the tag name.
213 If second arg NEXT is non-nil (interactively, with prefix arg),
214searches for the next tag in the tag table
215that matches the tagname used in the previous find-tag.
216
217See documentation of variable tags-file-name."
218 (interactive (if current-prefix-arg
219 '(nil t)
220 (find-tag-tag "Find tag: ")))
e15bee3f 221 (let ((tagbuf (find-tag-noselect tagname nil next)))
ff1f0fa6 222 (if other-window
e15bee3f
ER
223 (switch-to-buffer-other-window tagbuf)
224 (switch-to-buffer tagbuf))
225 )
ff1f0fa6
JB
226 (setq tags-loop-form '(find-tag nil t))
227 ;; Return t in case used as the tags-loop-form.
228 t)
229
c086701a
JB
230;;;###autoload
231(define-key esc-map "." 'find-tag)
232
233;;;###autoload
ff1f0fa6
JB
234(defun find-tag-other-window (tagname &optional next)
235 "Find tag (in current tag table) whose name contains TAGNAME.
236 Selects the buffer that the tag is contained in in another window
237and puts point at its definition.
238 If TAGNAME is a null string, the expression in the buffer
239around or before point is used as the tag name.
240 If second arg NEXT is non-nil (interactively, with prefix arg),
241searches for the next tag in the tag table
242that matches the tagname used in the previous find-tag.
243
244See documentation of variable tags-file-name."
245 (interactive (if current-prefix-arg
246 '(nil t)
247 (find-tag-tag "Find tag other window: ")))
248 (find-tag tagname next t))
c086701a
JB
249;;;###autoload
250(define-key ctl-x-4-map "." 'find-tag-other-window)
ff1f0fa6 251
daa37602
JB
252;;;###autoload
253(defun find-tag-other-frame (tagname &optional next)
254 "Find tag (in current tag table) whose name contains TAGNAME.
255 Selects the buffer that the tag is contained in in another frame
256and puts point at its definition.
257 If TAGNAME is a null string, the expression in the buffer
258around or before point is used as the tag name.
259 If second arg NEXT is non-nil (interactively, with prefix arg),
260searches for the next tag in the tag table
261that matches the tagname used in the previous find-tag.
262
263See documentation of variable tags-file-name."
264 (interactive (if current-prefix-arg
265 '(nil t)
266 (find-tag-tag "Find tag other window: ")))
267 (let ((pop-up-screens t))
268 (find-tag tagname next t)))
269;;;###autoload
270(define-key ctl-x-5-map "." 'find-tag-other-frame)
271
ff1f0fa6
JB
272(defvar next-file-list nil
273 "List of files for next-file to process.")
274
c086701a 275;;;###autoload
ff1f0fa6
JB
276(defun next-file (&optional initialize)
277 "Select next file among files in current tag table.
278Non-nil argument (prefix arg, if interactive)
279initializes to the beginning of the list of files in the tag table."
280 (interactive "P")
281 (if initialize
282 (setq next-file-list (tag-table-files)))
283 (or next-file-list
284 (error "All files processed."))
285 (find-file (car next-file-list))
286 (setq next-file-list (cdr next-file-list)))
287
288(defvar tags-loop-form nil
289 "Form for tags-loop-continue to eval to process one file.
290If it returns nil, it is through with one file; move on to next.")
291
c086701a 292;;;###autoload
ff1f0fa6
JB
293(defun tags-loop-continue (&optional first-time)
294 "Continue last \\[tags-search] or \\[tags-query-replace] command.
295Used noninteractively with non-nil argument
296to begin such a command. See variable tags-loop-form."
297 (interactive)
298 (if first-time
299 (progn (next-file t)
300 (goto-char (point-min))))
301 (while (not (eval tags-loop-form))
302 (next-file)
303 (message "Scanning file %s..." buffer-file-name)
304 (goto-char (point-min))))
c086701a
JB
305;;;###autoload
306(define-key esc-map "," 'tags-loop-continue)
ff1f0fa6 307
c086701a 308;;;###autoload
ff1f0fa6
JB
309(defun tags-search (regexp)
310 "Search through all files listed in tag table for match for REGEXP.
311Stops when a match is found.
312To continue searching for next match, use command \\[tags-loop-continue].
313
314See documentation of variable tags-file-name."
315 (interactive "sTags search (regexp): ")
316 (if (and (equal regexp "")
317 (eq (car tags-loop-form) 're-search-forward))
318 (tags-loop-continue nil)
319 (setq tags-loop-form
320 (list 're-search-forward regexp nil t))
321 (tags-loop-continue t)))
322
c086701a 323;;;###autoload
ff1f0fa6
JB
324(defun tags-query-replace (from to &optional delimited)
325 "Query-replace-regexp FROM with TO through all files listed in tag table.
326Third arg DELIMITED (prefix arg) means replace only word-delimited matches.
327If you exit (C-G or ESC), you can resume the query-replace
328with the command \\[tags-loop-continue].
329
330See documentation of variable tags-file-name."
331 (interactive "sTags query replace (regexp): \nsTags query replace %s by: \nP")
332 (setq tags-loop-form
333 (list 'and (list 'save-excursion
334 (list 're-search-forward from nil t))
335 (list 'not (list 'perform-replace from to t t
336 (not (null delimited))))))
337 (tags-loop-continue t))
338
c086701a 339;;;###autoload
ff1f0fa6
JB
340(defun list-tags (string)
341 "Display list of tags in file FILE.
342FILE should not contain a directory spec
343unless it has one in the tag table."
344 (interactive "sList tags (in file): ")
345 (with-output-to-temp-buffer "*Tags List*"
346 (princ "Tags in file ")
347 (princ string)
348 (terpri)
349 (save-excursion
350 (visit-tags-table-buffer)
351 (goto-char 1)
352 (search-forward (concat "\f\n" string ","))
353 (forward-line 1)
354 (while (not (or (eobp) (looking-at "\f")))
355 (princ (buffer-substring (point)
356 (progn (skip-chars-forward "^\177")
357 (point))))
358 (terpri)
359 (forward-line 1)))))
360
c086701a 361;;;###autoload
ff1f0fa6
JB
362(defun tags-apropos (string)
363 "Display list of all tags in tag table REGEXP matches."
364 (interactive "sTag apropos (regexp): ")
365 (with-output-to-temp-buffer "*Tags List*"
366 (princ "Tags matching regexp ")
367 (prin1 string)
368 (terpri)
369 (save-excursion
370 (visit-tags-table-buffer)
371 (goto-char 1)
372 (while (re-search-forward string nil t)
373 (beginning-of-line)
374 (princ (buffer-substring (point)
375 (progn (skip-chars-forward "^\177")
376 (point))))
377 (terpri)
378 (forward-line 1)))))
aa228418 379
49116ac0
JB
380(provide 'etags)
381
c0274f38 382;;; etags.el ends here