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