428
|
1 ;;; etags.el --- etags facility for Emacs
|
|
2
|
|
3 ;; Copyright 1985, 1986, 1988, 1990, 1997 Free Software Foundation, Inc.
|
|
4
|
|
5 ;; Author: Their Name is Legion (see list below)
|
|
6 ;; Maintainer: XEmacs Development Team
|
|
7 ;; Keywords: tools
|
|
8
|
|
9 ;; This file is part of XEmacs.
|
|
10
|
|
11 ;; XEmacs is free software; you can redistribute it and/or modify it
|
|
12 ;; under the terms of the GNU General Public License as published by
|
|
13 ;; the Free Software Foundation; either version 2, or (at your option)
|
|
14 ;; any later version.
|
|
15
|
|
16 ;; XEmacs is distributed in the hope that it will be useful, but
|
|
17 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
19 ;; General Public License for more details.
|
|
20
|
|
21 ;; You should have received a copy of the GNU General Public License
|
|
22 ;; along with XEmacs; see the file COPYING. If not, write to the
|
|
23 ;; Free Software Foundation, 59 Temple Place - Suite 330,
|
|
24 ;; Boston, MA 02111-1307, USA.
|
|
25
|
|
26 ;;; Synched up with: Not synched with FSF.
|
|
27
|
|
28 ;;; Commentary:
|
|
29
|
|
30 ;; This file is completely different from FSF's etags.el. It appears
|
|
31 ;; that an early version of this file (tags.el) has been rewritten by
|
|
32 ;; two different people; we got one, FSF got the other. Various
|
|
33 ;; people have said that our version is better and faster.
|
|
34
|
|
35 ;; TODO:
|
|
36 ;; - DOCUMENT!
|
|
37
|
|
38 ;; Derived from the original lisp/tags.el.
|
|
39
|
|
40 ;; Ideas and code from the work of the following people:
|
|
41 ;; Andy Norman <ange@hplb.hpl.hp.com>, author of ange-tags.el
|
|
42 ;; Ramana Rao <rao@arisia.xerox.com>
|
|
43 ;; John Sturdy <jcgs@harlqn.co.uk>, author of tags-helper.el
|
|
44 ;; Henry Kautz <kautz@allegra.att.com>, author of tag-completion.el
|
|
45 ;; Dan LaLiberte <liberte@cs.uiuc.edu>, author of local-tags.el
|
|
46 ;; Tom Dietterich <tgd@turing.cs.orst.edu>, author of quest.el
|
|
47 ;; The author(s) of lisp/simple.el
|
|
48 ;; Duke Briscoe <briscoe@cs.yale.edu>
|
|
49 ;; Lynn Slater <lrs@indetech.com>, author of location.el
|
|
50 ;; Shinichirou Sugou <shin@sgtp.apple.juice.or.jp>
|
|
51 ;; an unidentified anonymous elisp hacker
|
|
52 ;; Kyle Jones <kyle_jones@wonderworks.com>
|
|
53 ;; added "Exact match, then inexact" code
|
|
54 ;; added support for include directive.
|
|
55 ;; Hrvoje Niksic <hniksic@xemacs.org>
|
|
56 ;; various changes.
|
|
57
|
|
58
|
|
59 ;;; User variables.
|
|
60
|
|
61 (defgroup etags nil
|
|
62 "Etags facility for Emacs.
|
|
63 Using etags, you can create tag tables for any number of files, and
|
|
64 easily access the symbols in those files, using the `\\[find-tag]'
|
|
65 command."
|
|
66 :prefix "tags-"
|
|
67 :group 'tools)
|
|
68
|
|
69
|
|
70 (defcustom tags-build-completion-table 'ask
|
|
71 "*If this variable is nil, then tags completion is disabled.
|
|
72 If it is t, then things which prompt for tags will do so with completion
|
|
73 across all known tags.
|
|
74 If it is the symbol `ask', you will be asked whether each tags table
|
|
75 should be added to the completion list as it is read in. (With the
|
|
76 exception that for very small tags tables, you will not be asked,
|
|
77 since they can be parsed quickly.)"
|
|
78 :type '(choice (const :tag "Disabled" nil)
|
|
79 (const :tag "Complete All" t)
|
|
80 (const :tag "Ask" ask))
|
|
81 :group 'etags)
|
|
82
|
|
83 (defcustom tags-always-exact nil
|
|
84 "*If this variable is non-nil, then tags always looks for exact matches.
|
|
85 If it is nil (the default), tags will first go through exact matches,
|
|
86 then through the non-exact ones."
|
|
87 :type 'boolean
|
|
88 :group 'etags)
|
|
89
|
|
90 (defcustom tag-table-alist nil
|
|
91 "*A list which determines which tags files are active for a buffer.
|
|
92 This is not really an association list, in that all elements are
|
|
93 checked. The CAR of each element of this list is a pattern against
|
|
94 which the buffer's file name is compared; if it matches, then the CDR
|
|
95 of the list should be the name of the tags table to use. If more than
|
|
96 one element of this list matches the buffer's file name, then all of
|
|
97 the associated tags tables will be used. Earlier ones will be
|
|
98 searched first.
|
|
99
|
|
100 If the CAR of elements of this list are strings, then they are treated
|
|
101 as regular-expressions against which the file is compared (like the
|
|
102 auto-mode-alist). If they are not strings, then they are evaluated.
|
|
103 If they evaluate to non-nil, then the current buffer is considered to
|
|
104 match.
|
|
105
|
|
106 If the CDR of the elements of this list are strings, then they are
|
|
107 assumed to name a TAGS file. If they name a directory, then the string
|
|
108 \"TAGS\" is appended to them to get the file name. If they are not
|
|
109 strings, then they are evaluated, and must return an appropriate string.
|
|
110
|
|
111 For example:
|
|
112 (setq tag-table-alist
|
|
113 '((\"/usr/src/public/perl/\" . \"/usr/src/public/perl/perl-3.0/\")
|
|
114 (\"\\\\.el$\" . \"/usr/local/emacs/src/\")
|
|
115 (\"/jbw/gnu/\" . \"/usr15/degree/stud/jbw/gnu/\")
|
|
116 (\"\" . \"/usr/local/emacs/src/\")
|
|
117 ))
|
|
118
|
|
119 This means that anything in the /usr/src/public/perl/ directory should use
|
|
120 the TAGS file /usr/src/public/perl/perl-3.0/TAGS; and file ending in .el should
|
|
121 use the TAGS file /usr/local/emacs/src/TAGS; and anything in or below the
|
|
122 directory /jbw/gnu/ should use the TAGS file /usr15/degree/stud/jbw/gnu/TAGS.
|
|
123 A file called something like \"/usr/jbw/foo.el\" would use both the TAGS files
|
|
124 /usr/local/emacs/src/TAGS and /usr15/degree/stud/jbw/gnu/TAGS (in that order)
|
|
125 because it matches both patterns.
|
|
126
|
|
127 If the buffer-local variable `buffer-tag-table' is set, then it names a tags
|
|
128 table that is searched before all others when find-tag is executed from this
|
|
129 buffer.
|
|
130
|
|
131 If there is a file called \"TAGS\" in the same directory as the file in
|
|
132 question, then that tags file will always be used as well (after the
|
|
133 `buffer-tag-table' but before the tables specified by this list.)
|
|
134
|
|
135 If the variable tags-file-name is set, then the tags file it names will apply
|
|
136 to all buffers (for backwards compatibility.) It is searched first."
|
|
137 :type '(repeat (cons :format "%v"
|
|
138 (choice :value ""
|
|
139 (regexp :tag "Buffer regexp")
|
|
140 sexp)
|
|
141 (choice :value ""
|
|
142 (string :tag "Tag file or directory")
|
|
143 sexp)))
|
|
144 :group 'etags)
|
|
145
|
|
146 (defvar buffer-tag-table nil
|
|
147 "*The additional name of one TAGS table to be used for this buffer.
|
|
148 You can set this with `\\[set-buffer-tag-table]'. See the documentation
|
|
149 for the variable `tag-table-alist' for more information.")
|
|
150 (make-variable-buffer-local 'buffer-tag-table)
|
|
151
|
|
152 (defvar tags-file-name nil
|
|
153 "The name of the tags-table used by all buffers.
|
|
154 This is for backwards compatibility, and is largely supplanted by the
|
|
155 variable tag-table-alist.")
|
|
156
|
|
157 (defcustom tags-auto-read-changed-tag-files nil
|
|
158 "*If non-nil, always re-read changed TAGS file without prompting.
|
|
159 If nil, prompt whether to re-read the changed TAGS file."
|
|
160 :type 'boolean
|
|
161 :group 'etags)
|
|
162
|
|
163 (defcustom make-tags-files-invisible nil
|
|
164 "*If non-nil, TAGS-files will not show up in buffer-lists or be
|
|
165 selectable (or deletable.)"
|
|
166 :type 'boolean
|
|
167 :group 'etags)
|
|
168
|
|
169 (defcustom tags-search-nuke-uninteresting-buffers t
|
|
170 "*If non-nil, keep newly-visited files if they contain the search target.
|
|
171 This affects the `tags-search' and `tags-query-replace' commands."
|
|
172 :type 'boolean
|
|
173 :group 'etags)
|
|
174
|
502
|
175 (defcustom tags-check-parent-directories-for-tag-files t
|
|
176 "*If non-nil, look for TAGS files in all parent directories."
|
|
177 :type 'boolean
|
|
178 :group 'etags)
|
|
179
|
428
|
180
|
|
181 ;; Buffer tag tables.
|
|
182
|
|
183 (defun buffer-tag-table-list ()
|
|
184 "Returns a list (ordered) of the tags tables which should be used for
|
|
185 the current buffer."
|
|
186 (let (result)
|
|
187 ;; Explicitly set buffer-tag-table
|
|
188 (when buffer-tag-table
|
|
189 (push buffer-tag-table result))
|
|
190 ;; Current directory
|
|
191 (when (file-readable-p (concat default-directory "TAGS"))
|
|
192 (push (concat default-directory "TAGS") result))
|
502
|
193 ;; Parent directories
|
|
194 (when tags-check-parent-directories-for-tag-files
|
|
195 (let ((cur default-directory))
|
593
|
196 ;; Fuck! Shouldn't there be a more obvious portable way
|
|
197 ;; to determine if we're the root? Shouldn't we have a
|
|
198 ;; proper path manipulation API? Do you know how many
|
|
199 ;; god-damn bugs are lurking out there because of Unix/
|
|
200 ;; Windows differences? And how much code is littered
|
|
201 ;; with stuff such as 10 lines down from here?
|
|
202 (while (not (and (equal (file-name-as-directory cur) cur)
|
|
203 (equal (directory-file-name cur) cur)))
|
|
204 (setq cur (expand-file-name ".." cur))
|
502
|
205 (let ((parent-tag-file (expand-file-name "TAGS" cur)))
|
|
206 (when (file-readable-p parent-tag-file)
|
|
207 (push parent-tag-file result))))))
|
428
|
208 ;; tag-table-alist
|
442
|
209 (let* ((key (or buffer-file-name
|
|
210 (concat default-directory (buffer-name))))
|
|
211 (key (if (eq system-type 'windows-nt)
|
|
212 (replace-in-string key "\\\\" "/")
|
|
213 key))
|
|
214 expression)
|
428
|
215 (dolist (item tag-table-alist)
|
|
216 (setq expression (car item))
|
|
217 ;; If the car of the alist item is a string, apply it as a regexp
|
|
218 ;; to the buffer-file-name. Otherwise, evaluate it. If the
|
|
219 ;; regexp matches, or the expression evaluates non-nil, then this
|
|
220 ;; item in tag-table-alist applies to this buffer.
|
|
221 (when (if (stringp expression)
|
|
222 (string-match expression key)
|
|
223 (ignore-errors
|
|
224 (eval expression)))
|
|
225 ;; Now evaluate the cdr of the alist item to get the name of
|
|
226 ;; the tag table file.
|
|
227 (setq expression (ignore-errors
|
|
228 (eval (cdr item))))
|
|
229 (if (stringp expression)
|
|
230 (push expression result)
|
|
231 (error "Expression in tag-table-alist evaluated to non-string")))))
|
|
232 (setq result
|
|
233 (mapcar
|
|
234 (lambda (name)
|
|
235 (when (file-directory-p name)
|
|
236 (setq name (concat (file-name-as-directory name) "TAGS")))
|
|
237 (and (file-readable-p name)
|
|
238 ;; get-tag-table-buffer has side-effects
|
|
239 (symbol-value-in-buffer 'buffer-file-name
|
|
240 (get-tag-table-buffer name))))
|
|
241 result))
|
|
242 (setq result (delq nil result))
|
|
243 ;; If no TAGS file has been found, ask the user explicitly.
|
|
244 ;; #### tags-file-name is *evil*.
|
|
245 (or result tags-file-name
|
|
246 (call-interactively 'visit-tags-table))
|
|
247 (when tags-file-name
|
|
248 (setq result (nconc result (list tags-file-name))))
|
|
249 (or result (error "Buffer has no associated tag tables"))
|
|
250 (delete-duplicates (nreverse result) :test 'equal)))
|
|
251
|
|
252 ;;;###autoload
|
|
253 (defun visit-tags-table (file)
|
|
254 "Tell tags commands to use tags table file FILE when all else fails.
|
|
255 FILE should be the name of a file created with the `etags' program.
|
|
256 A directory name is ok too; it means file TAGS in that directory."
|
|
257 (interactive (list (read-file-name "Visit tags table: (default TAGS) "
|
|
258 default-directory
|
|
259 (expand-file-name "TAGS" default-directory)
|
|
260 t)))
|
|
261 (if (string-equal file "")
|
|
262 (setq tags-file-name nil)
|
|
263 (setq file (expand-file-name file))
|
|
264 (when (file-directory-p file)
|
|
265 (setq file (expand-file-name "TAGS" file)))
|
|
266 ;; It used to be that, if a user pressed RET by mistake, the bogus
|
|
267 ;; `tags-file-name' would remain, causing the error at
|
|
268 ;; `buffer-tag-table'.
|
|
269 (when (file-exists-p file)
|
|
270 (setq tags-file-name file))))
|
|
271
|
|
272 (defun set-buffer-tag-table (file)
|
|
273 "In addition to the tags tables specified by the variable `tag-table-alist',
|
|
274 each buffer can have one additional table. This command sets that.
|
|
275 See the documentation for the variable `tag-table-alist' for more information."
|
|
276 (interactive
|
|
277 (list
|
|
278 (read-file-name "Visit tags table: (directory sufficient) "
|
|
279 nil default-directory t)))
|
|
280 (or file (error "No TAGS file name supplied"))
|
|
281 (setq file (expand-file-name file))
|
|
282 (when (file-directory-p file)
|
|
283 (setq file (expand-file-name "TAGS" file)))
|
|
284 (or (file-exists-p file) (error "TAGS file missing: %s" file))
|
|
285 (setq buffer-tag-table file))
|
|
286
|
|
287
|
|
288 ;; Manipulating the tag table buffer
|
|
289
|
|
290 (defconst tag-table-completion-status nil
|
|
291 "Indicates whether a completion table has been built.
|
|
292 Either nil, t, or `disabled'.")
|
|
293 (make-variable-buffer-local 'tag-table-completion-status)
|
|
294
|
|
295 (defconst tag-table-files nil
|
|
296 "If the current buffer is a TAGS table, this holds a list of the files
|
|
297 referenced by this file, or nil if that hasn't been computed yet.")
|
|
298 (make-variable-buffer-local 'tag-table-files)
|
|
299
|
|
300 (defun get-tag-table-buffer (tag-table)
|
|
301 "Returns a buffer visiting the given TAGS table.
|
|
302 If appropriate, reverting the buffer, and possibly build a completion-table."
|
|
303 (or (stringp tag-table)
|
|
304 (error "Bad tags file name supplied: %s" tag-table))
|
|
305 ;; Remove symbolic links from name.
|
|
306 (setq tag-table (symlink-expand-file-name tag-table))
|
|
307 (let (buf build-completion check-name)
|
|
308 (setq buf (get-file-buffer tag-table))
|
|
309 (unless buf
|
|
310 (if (file-readable-p tag-table)
|
|
311 (setq buf (find-file-noselect tag-table)
|
|
312 check-name t)
|
|
313 (error "No such tags file: %s" tag-table)))
|
|
314 (with-current-buffer buf
|
|
315 ;; Make the TAGS buffer invisible.
|
|
316 (when (and check-name
|
|
317 make-tags-files-invisible
|
|
318 (string-match "\\`[^ ]" (buffer-name)))
|
|
319 (rename-buffer (generate-new-buffer-name
|
|
320 (concat " " (buffer-name)))))
|
|
321 (or (verify-visited-file-modtime buf)
|
|
322 (cond ((or tags-auto-read-changed-tag-files
|
|
323 (yes-or-no-p
|
|
324 (format "Tags file %s has changed, read new contents? "
|
|
325 tag-table)))
|
|
326 (when tags-auto-read-changed-tag-files
|
|
327 (message "Tags file %s has changed, reading new contents..."
|
|
328 tag-table))
|
|
329 (revert-buffer t t)
|
|
330 (when (eq tag-table-completion-status t)
|
|
331 (setq tag-table-completion-status nil))
|
|
332 (setq tag-table-files nil))))
|
|
333 (or (eq (char-after 1) ?\f)
|
|
334 (error "File %s not a valid tags file" tag-table))
|
|
335 (or (memq tag-table-completion-status '(t disabled))
|
|
336 (setq build-completion t))
|
|
337 (when build-completion
|
|
338 (if (ecase tags-build-completion-table
|
|
339 ((nil) nil)
|
|
340 ((t) t)
|
|
341 ((ask)
|
|
342 ;; don't bother asking for small ones
|
|
343 (or (< (buffer-size) 20000)
|
|
344 (y-or-n-p
|
|
345 (format "Build tag completion table for %s? "
|
|
346 tag-table)))))
|
|
347 ;; The user wants to build the table:
|
|
348 (condition-case nil
|
|
349 (progn
|
|
350 (add-to-tag-completion-table)
|
|
351 (setq tag-table-completion-status t))
|
|
352 ;; Allow user to C-g out correctly
|
|
353 (quit
|
|
354 (message "Tags completion table construction aborted")
|
|
355 (setq tag-table-completion-status nil
|
|
356 quit-flag t)
|
|
357 t))
|
|
358 ;; The table is verboten.
|
|
359 (setq tag-table-completion-status 'disabled))))
|
|
360 buf))
|
|
361
|
|
362 (defun file-of-tag ()
|
|
363 "Return the file name of the file whose tags point is within.
|
|
364 Assumes the tag table is the current buffer.
|
|
365 File name returned is relative to tag table file's directory."
|
|
366 (let ((opoint (point))
|
|
367 prev size)
|
|
368 (save-excursion
|
|
369 (goto-char (point-min))
|
|
370 (while (< (point) opoint)
|
|
371 (forward-line 1)
|
|
372 (end-of-line)
|
|
373 (skip-chars-backward "^,\n")
|
|
374 (setq prev (point)
|
|
375 size (read (current-buffer)))
|
|
376 (goto-char prev)
|
|
377 (forward-line 1)
|
|
378 ;; New include syntax
|
|
379 ;; filename,include
|
|
380 ;; tacked on to the end of a tag file means use filename
|
|
381 ;; as a tag file before giving up.
|
|
382 ;; Skip it here.
|
|
383 (unless (eq size 'include)
|
|
384 (forward-char size)))
|
|
385 (goto-char (1- prev))
|
|
386 (buffer-substring (point) (point-at-bol)))))
|
|
387
|
|
388 (defun tag-table-include-files ()
|
|
389 "Return all file names associated with `include' directives in a tag buffer."
|
|
390 ;; New include syntax
|
|
391 ;; filename,include
|
|
392 ;; tacked on to the end of a tag file means use filename as a
|
|
393 ;; tag file before giving up.
|
|
394 (let ((files nil))
|
|
395 (save-excursion
|
|
396 (goto-char (point-min))
|
|
397 (while (re-search-forward "\f\n\\(.*\\),include$" nil t)
|
|
398 (push (match-string 1) files)))
|
|
399 files))
|
|
400
|
|
401 (defun tag-table-files (tag-table)
|
|
402 "Returns a list of the files referenced by the named TAGS table."
|
|
403 (with-current-buffer (get-tag-table-buffer tag-table)
|
|
404 (unless tag-table-files
|
|
405 (let (files prev size)
|
|
406 (goto-char (point-min))
|
|
407 (while (not (eobp))
|
|
408 (forward-line 1)
|
|
409 (end-of-line)
|
|
410 (skip-chars-backward "^,\n")
|
|
411 (setq prev (point)
|
|
412 size (read (current-buffer)))
|
|
413 (goto-char prev)
|
|
414 (push (expand-file-name (buffer-substring (1- (point))
|
|
415 (point-at-bol))
|
|
416 default-directory)
|
|
417 files)
|
|
418 (forward-line 1)
|
|
419 (forward-char size))
|
|
420 (setq tag-table-files (nreverse files))))
|
|
421 tag-table-files))
|
|
422
|
|
423 ;; #### should this be on previous page?
|
|
424 (defun buffer-tag-table-files ()
|
|
425 "Returns a list of all files referenced by all TAGS tables that
|
|
426 this buffer uses."
|
444
|
427 (apply #'append
|
428
|
428 (mapcar #'tag-table-files (buffer-tag-table-list))))
|
|
429
|
|
430
|
|
431 ;; Building the completion table
|
|
432
|
|
433 ;; Test cases for building completion table; must handle these properly:
|
|
434 ;; Lisp_Int, XSETINT, current_column 60,2282
|
|
435 ;; Lisp_Int, XSETINT, point>NumCharacters ? 0 : CharAt(363,9935
|
|
436 ;; Lisp_Int, XSETINT, point<=FirstCharacter ? 0 : CharAt(366,10108
|
|
437 ;; point<=FirstCharacter || CharAt(378,10630
|
|
438 ;; point>NumCharacters || CharAt(382,10825
|
|
439 ;; DEFUN ("x-set-foreground-color", Fx_set_foreground_color,191,4562
|
|
440 ;; DEFUN ("x-set-foreground-color", Fx_set_foreground_color,191,4562
|
|
441 ;; DEFUN ("*", Ftimes,1172,32079
|
|
442 ;; DEFUN ("/=", Fneq,1035,28839
|
|
443 ;; defun_internal 4199,101362
|
|
444 ;; int pure[PURESIZE / sizeof 53,1564
|
|
445 ;; char staticvec1[NSTATICS * sizeof 667,17608
|
|
446 ;; Date: 04 May 87 23:53:11 PDT 26,1077
|
|
447 ;; #define anymacroname(324,4344
|
|
448 ;; (define-key ctl-x-map 311,11784
|
|
449 ;; (define-abbrev-table 'c-mode-abbrev-table 24,1016
|
|
450 ;; static char *skip_white(116,3443
|
|
451 ;; static foo 348,11643
|
|
452 ;; (defun texinfo-insert-@code 91,3358
|
|
453 ;; (defvar texinfo-kindex)29,1105
|
|
454 ;; (defun texinfo-format-\. 548,18376
|
|
455 ;; (defvar sm::menu-kludge-y 621,22726
|
|
456 ;; (defvar *mouse-drag-window* 103,3642
|
|
457 ;; (defun simula-back-level(317,11263
|
|
458 ;; } DPxAC,380,14024
|
|
459 ;; } BM_QCB;69,2990
|
|
460 ;; #define MTOS_DONE\t
|
|
461
|
|
462 ;; "^[^ ]+ +\\([^ ]+\\) "
|
|
463
|
|
464 ;; void *find_cactus_segment(116,2444
|
|
465 ;; void *find_pdb_segment(162,3688
|
|
466 ;; void init_dclpool(410,10739
|
|
467 ;; WORD insert_draw_command(342,8881
|
|
468 ;; void *req_pdbmem(579,15574
|
|
469
|
|
470 (defvar tag-completion-table (make-vector 511 0))
|
|
471
|
|
472 (defvar tag-symbol)
|
|
473 (defvar tag-table-symbol)
|
|
474 (defvar tag-symbol-tables)
|
|
475 (defvar buffer-tag-table-list)
|
|
476
|
|
477 (defmacro intern-tag-symbol (tag)
|
|
478 `(progn
|
|
479 (setq tag-symbol (intern ,tag tag-completion-table)
|
|
480 tag-symbol-tables (and (boundp tag-symbol)
|
|
481 (symbol-value tag-symbol)))
|
|
482 (or (memq tag-table-symbol tag-symbol-tables)
|
|
483 (set tag-symbol (cons tag-table-symbol tag-symbol-tables)))))
|
|
484
|
|
485 ;; Can't use "\\s " in these patterns because that will include newline
|
|
486 (defconst tags-DEFUN-pattern
|
|
487 "DEFUN[ \t]*(\"\\([^\"]+\\)\",[ \t]*\\(\\(\\sw\\|\\s_\\)+\\),\C-?")
|
|
488 (defconst tags-array-pattern ".*[ \t]+\\([^ \[]+\\)\\[")
|
|
489 (defconst tags-def-pattern
|
|
490 "\\(.*[ \t]+\\)?\\**\\(\\(\\sw\\|\\s_\\)+\\)[ ();,\t]*\C-?"
|
|
491 ;; "\\(.*[ \t]+\\)?\\(\\(\\sw\\|\\s_\\)+\\)[ ()]*\C-?"
|
|
492 ;; "\\(\\sw\\|\\s_\\)+[ ()]*\C-?"
|
|
493 )
|
|
494 (defconst tags-file-pattern "^\f\n\\(.+\\),[0-9]+\n")
|
|
495
|
|
496 ;; #### Should make it work with the `include' directive!
|
|
497 (defun add-to-tag-completion-table ()
|
|
498 "Sucks the current buffer (a TAGS table) into the completion-table."
|
|
499 (message "Adding %s to tags completion table..." buffer-file-name)
|
|
500 (goto-char (point-min))
|
|
501 (let ((tag-table-symbol (intern buffer-file-name tag-completion-table))
|
|
502 ;; tag-table-symbol is used by intern-tag-symbol
|
|
503 filename file-type name name2 tag-symbol
|
|
504 tag-symbol-tables
|
|
505 (case-fold-search nil))
|
|
506 ;; Loop over the files mentioned in the TAGS file for each file,
|
|
507 ;; try to find its major-mode, then process tags appropriately.
|
|
508 (while (looking-at tags-file-pattern)
|
|
509 (goto-char (match-end 0))
|
|
510 (setq filename (file-name-sans-versions (match-string 1))
|
|
511 ;; We used to check auto-mode-alist for the proper
|
|
512 ;; file-type. This was way too slow, as it had to process
|
|
513 ;; an enormous amount of regexps for each time. Now we
|
|
514 ;; use the shotgun approach with only two regexps.
|
|
515 file-type (cond ((string-match "\\.\\([cC]\\|cc\\|cxx\\)\\'"
|
|
516 filename)
|
|
517 'c-mode)
|
|
518 ((string-match "\\.\\(el\\|cl\\|lisp\\)\\'"
|
|
519 filename)
|
|
520 'lisp-mode)
|
|
521 ((string-match "\\.scm\\'" filename)
|
|
522 'scheme-mode)
|
|
523 (t nil)))
|
442
|
524 (defvar c-mode-syntax-table)
|
428
|
525 (set-syntax-table (cond ((and (eq file-type 'c-mode)
|
|
526 c-mode-syntax-table)
|
|
527 c-mode-syntax-table)
|
|
528 ((eq file-type 'lisp-mode)
|
|
529 lisp-mode-syntax-table)
|
|
530 (t (standard-syntax-table))))
|
|
531 ;; Clear loop variables.
|
|
532 (setq name nil name2 nil)
|
|
533 (lmessage 'progress "%s..." filename)
|
|
534 ;; Loop over the individual tag lines.
|
|
535 (while (not (or (eobp) (eq (char-after) ?\f)))
|
|
536 (cond ((and (eq file-type 'c-mode)
|
|
537 (looking-at "DEFUN[ \t]"))
|
|
538 ;; DEFUN
|
|
539 (or (looking-at tags-DEFUN-pattern)
|
|
540 (error "DEFUN doesn't fit pattern"))
|
|
541 (setq name (match-string 1)
|
|
542 name2 (match-string 2)))
|
|
543 ;;((looking-at "\\s ")
|
|
544 ;; skip probably bogus entry:
|
|
545 ;;)
|
|
546 ((and (eq file-type 'c-mode)
|
|
547 (looking-at ".*\\["))
|
|
548 ;; Array
|
|
549 (cond ((not (looking-at tags-array-pattern))
|
|
550 (message "array definition doesn't fit pattern")
|
|
551 (setq name nil))
|
|
552 (t
|
|
553 (setq name (match-string 1)))))
|
|
554 ((and (eq file-type 'scheme-mode)
|
|
555 (looking-at "\\s-*(\\s-*def\\sw*\\s-*(?\\s-*\\(\\(\\sw\\|\\s_\\|:\\)+\\))?\\s-*\C-?"))
|
|
556 ;; Something Schemish (is this really necessary??)
|
|
557 (setq name (match-string 1)))
|
|
558 ((looking-at tags-def-pattern)
|
|
559 ;; ???
|
|
560 (setq name (match-string 2))))
|
|
561 ;; add the tags we found to the completion table
|
|
562 (and name (intern-tag-symbol name))
|
|
563 (and name2 (intern-tag-symbol name2))
|
|
564 (forward-line 1)))
|
|
565 (or (eobp) (error "Bad TAGS file")))
|
|
566 (message "Adding %s to tags completion table...done" buffer-file-name))
|
|
567
|
|
568
|
|
569 ;; Interactive find-tag
|
|
570
|
|
571 (defvar find-tag-default-hook nil
|
|
572 "Function to call to create a default tag.
|
|
573 Make it buffer-local in a mode hook. The function is called with no
|
|
574 arguments.")
|
|
575
|
|
576 (defvar find-tag-hook nil
|
|
577 "*Function to call after a tag is found.
|
|
578 Make it buffer-local in a mode hook. The function is called with no
|
|
579 arguments.")
|
|
580
|
|
581 ;; Return a default tag to search for, based on the text at point.
|
|
582 (defun find-tag-default ()
|
|
583 (or (and (not (memq find-tag-default-hook '(nil find-tag-default)))
|
|
584 (condition-case data
|
|
585 (funcall find-tag-default-hook)
|
|
586 (error
|
|
587 (warn "Error in find-tag-default-hook signalled error: %s"
|
|
588 (error-message-string data))
|
|
589 nil)))
|
|
590 (symbol-near-point)))
|
|
591
|
|
592 ;; This function depends on the following symbols being bound properly:
|
|
593 ;; buffer-tag-table-list,
|
|
594 ;; tag-symbol-tables (value irrelevant, bound outside for efficiency)
|
|
595 (defun tag-completion-predicate (tag-symbol)
|
|
596 (and (boundp tag-symbol)
|
|
597 (setq tag-symbol-tables (symbol-value tag-symbol))
|
|
598 (catch 'found
|
|
599 (while tag-symbol-tables
|
|
600 (when (memq (car tag-symbol-tables) buffer-tag-table-list)
|
|
601 (throw 'found t))
|
|
602 (setq tag-symbol-tables (cdr tag-symbol-tables))))))
|
|
603
|
|
604 (defun buffer-tag-table-symbol-list ()
|
|
605 (mapcar (lambda (table-name)
|
|
606 (intern table-name tag-completion-table))
|
|
607 (buffer-tag-table-list)))
|
|
608
|
|
609 (defvar find-tag-history nil "History list for find-tag-tag.")
|
|
610
|
|
611 (defun find-tag-tag (prompt)
|
|
612 (let* ((default (find-tag-default))
|
|
613 (buffer-tag-table-list (buffer-tag-table-symbol-list))
|
|
614 tag-symbol-tables tag-name)
|
|
615 (setq tag-name
|
|
616 (completing-read
|
|
617 (if default
|
|
618 (format "%s(default %s) " prompt default)
|
|
619 prompt)
|
|
620 tag-completion-table 'tag-completion-predicate nil nil
|
440
|
621 'find-tag-history default))
|
|
622 tag-name))
|
428
|
623
|
|
624 (defvar last-tag-data nil
|
|
625 "Information for continuing a tag search.
|
|
626 Is of the form (TAG POINT MATCHING-EXACT TAG-TABLE TAG-TABLE ...).")
|
|
627
|
|
628 (defvar tags-loop-operate nil
|
|
629 "Form for `tags-loop-continue' to eval to change one file.")
|
|
630
|
|
631 (defvar tags-loop-scan
|
|
632 '(error "%s" (substitute-command-keys
|
|
633 "No \\[tags-search] or \\[tags-query-replace] in progress."))
|
|
634 "Form for `tags-loop-continue' to eval to scan one file.
|
|
635 If it returns non-nil, this file needs processing by evalling
|
|
636 \`tags-loop-operate'. Otherwise, move on to the next file.")
|
|
637
|
|
638 (autoload 'get-symbol-syntax-table "symbol-syntax")
|
|
639
|
|
640 (defun find-tag-internal (tagname)
|
487
|
641
|
428
|
642 (let ((next (null tagname))
|
|
643 (tmpnext (null tagname))
|
|
644 ;; If tagname is a list: (TAGNAME), this indicates
|
|
645 ;; requiring an exact symbol match.
|
|
646 (exact (or tags-always-exact (consp tagname)))
|
|
647 (normal-syntax-table (syntax-table))
|
|
648 (exact-syntax-table (get-symbol-syntax-table (syntax-table)))
|
|
649 tag-table-currently-matching-exact
|
|
650 tag-target exact-tagname
|
487
|
651 tag-tables tag-table-point file linebeg line startpos buf
|
428
|
652 offset found pat syn-tab)
|
|
653 (when (consp tagname)
|
|
654 (setq tagname (car tagname)))
|
|
655 (cond (next
|
|
656 (setq tagname (car last-tag-data))
|
|
657 (setq tag-table-currently-matching-exact
|
|
658 (car (cdr (cdr last-tag-data)))))
|
|
659 (t
|
|
660 (setq tag-table-currently-matching-exact t)))
|
|
661 ;; \_ in the tagname is used to indicate a symbol boundary.
|
442
|
662 (setq exact-tagname (format "\C-?\\_%s\\_\C-a\\|\\_%s\\_" tagname tagname))
|
428
|
663 (while (string-match "\\\\_" exact-tagname)
|
|
664 (aset exact-tagname (1- (match-end 0)) ?b))
|
|
665 (save-excursion
|
|
666 (catch 'found
|
|
667 ;; Loop searching for exact matches and then inexact matches.
|
|
668 (while (not (eq tag-table-currently-matching-exact 'neither))
|
|
669 (cond (tmpnext
|
|
670 (setq tag-tables (cdr (cdr (cdr last-tag-data)))
|
|
671 tag-table-point (car (cdr last-tag-data)))
|
|
672 ;; Start from the beginning of the table list on the
|
|
673 ;; next iteration of the loop.
|
|
674 (setq tmpnext nil))
|
|
675 (t
|
|
676 (setq tag-tables (buffer-tag-table-list)
|
|
677 tag-table-point 1)))
|
|
678 (if tag-table-currently-matching-exact
|
|
679 (setq tag-target exact-tagname
|
|
680 syn-tab exact-syntax-table)
|
|
681 (setq tag-target tagname
|
|
682 syn-tab normal-syntax-table))
|
|
683 (with-search-caps-disable-folding tag-target t
|
|
684 (while tag-tables
|
|
685 (set-buffer (get-tag-table-buffer (car tag-tables)))
|
|
686 (bury-buffer (current-buffer))
|
|
687 (goto-char (or tag-table-point (point-min)))
|
|
688 (setq tag-table-point nil)
|
|
689 (letf (((syntax-table) syn-tab)
|
|
690 (case-fold-search nil))
|
|
691 ;; #### should there be support for non-regexp
|
|
692 ;; tag searches?
|
|
693 (while (re-search-forward tag-target nil t)
|
|
694 (and (save-match-data
|
442
|
695 (save-excursion
|
|
696 (goto-char (match-beginning 0))
|
|
697 (looking-at "[^\n\C-?]*\C-?")))
|
428
|
698 ;; If we're looking for inexact matches, skip
|
|
699 ;; exact matches since we've visited them
|
|
700 ;; already.
|
|
701 (or tag-table-currently-matching-exact
|
|
702 (letf (((syntax-table) exact-syntax-table))
|
|
703 (save-excursion
|
|
704 (goto-char (match-beginning 0))
|
|
705 (not (looking-at exact-tagname)))))
|
|
706 (throw 'found t))))
|
|
707 (setq tag-tables
|
|
708 (nconc (tag-table-include-files) (cdr tag-tables)))))
|
|
709 (if (and (not exact) (eq tag-table-currently-matching-exact t))
|
|
710 (setq tag-table-currently-matching-exact nil)
|
|
711 (setq tag-table-currently-matching-exact 'neither)))
|
|
712 (error "No %sentries %s %s"
|
|
713 (if next "more " "")
|
|
714 (if exact "matching" "containing")
|
|
715 tagname))
|
442
|
716 (beginning-of-line)
|
487
|
717
|
|
718 ;; from here down, synched with FSF 20.7
|
|
719 ;; etags-snarf-tag and etags-goto-tag-location. --ben
|
|
720
|
|
721 (if (save-excursion
|
|
722 (forward-line -1)
|
|
723 (looking-at "\f\n"))
|
|
724 (progn
|
|
725 ;; The match was for a source file name, not any tag
|
|
726 ;; within a file. Give text of t, meaning to go exactly
|
|
727 ;; to the location we specify, the beginning of the file.
|
|
728 (setq linebeg t
|
|
729 line nil
|
|
730 startpos 1)
|
|
731 (setq file
|
|
732 (expand-file-name (file-of-tag)
|
|
733 ;; In XEmacs, this needs to be
|
|
734 ;; relative to:
|
|
735 (or (file-name-directory (car tag-tables))
|
|
736 "./"))))
|
|
737 (search-forward "\C-?")
|
|
738 (setq file
|
|
739 (expand-file-name (file-of-tag)
|
|
740 ;; In XEmacs, this needs to be
|
|
741 ;; relative to:
|
|
742 (or (file-name-directory (car tag-tables))
|
|
743 "./")))
|
|
744 (setq linebeg (buffer-substring (1- (point)) (point-at-bol)))
|
|
745 ;; Skip explicit tag name if present.
|
|
746 (search-forward "\001" (save-excursion (forward-line 1) (point)) t)
|
|
747 (if (looking-at "[0-9]")
|
|
748 (setq line (string-to-int (buffer-substring
|
|
749 (point)
|
|
750 (progn (skip-chars-forward "0-9")
|
|
751 (point))))))
|
|
752 (search-forward ",")
|
|
753 (if (looking-at "[0-9]")
|
|
754 (setq startpos (string-to-int (buffer-substring
|
|
755 (point)
|
|
756 (progn (skip-chars-forward "0-9")
|
|
757 (point)))))))
|
|
758 ;; Leave point on the next line of the tags file.
|
|
759 (forward-line 1)
|
428
|
760 (setq last-tag-data
|
|
761 (nconc (list tagname (point) tag-table-currently-matching-exact)
|
|
762 tag-tables))
|
|
763 (setq buf (find-file-noselect file))
|
487
|
764
|
|
765 ;; LINEBEG is the initial part of a line containing the tag and
|
|
766 ;; STARTPOS is the character position of LINEBEG within the file
|
|
767 ;; (starting from 1); LINE is the line number. If LINEBEG is t,
|
|
768 ;; it means the tag refers to exactly LINE or STARTPOS
|
|
769 ;; (whichever is present, LINE having preference, no searching).
|
|
770 ;; Either LINE or STARTPOS may be nil; STARTPOS is used if
|
|
771 ;; present. If the tag isn't exactly at the given position then
|
|
772 ;; look around that position using a search window which expands
|
|
773 ;; until it hits the start of file.
|
|
774
|
428
|
775 (with-current-buffer buf
|
|
776 (save-excursion
|
|
777 (save-restriction
|
|
778 (widen)
|
487
|
779 (if (eq linebeg t)
|
|
780 ;; Direct file tag.
|
|
781 (cond (line (goto-line line))
|
|
782 (startpos (goto-char startpos))
|
|
783 (t (error "etags.el BUG: bogus direct file tag")))
|
|
784 ;; Here we search for PAT in the range [STARTPOS - OFFSET,
|
|
785 ;; STARTPOS + OFFSET], with increasing values of OFFSET.
|
|
786 ;;
|
|
787 ;; We used to set the initial offset to 1000, but the
|
|
788 ;; actual sources show that finer-grained control is
|
|
789 ;; needed (e.g. two `hash_string's in src/symbols.c.) So,
|
|
790 ;; I changed 1000 to 100, and (* 3 offset) to (* 5 offset).
|
|
791 (setq offset 100)
|
|
792 (setq pat (concat (if (eq selective-display t)
|
|
793 "\\(^\\|\^m\\)" "^")
|
|
794 (regexp-quote linebeg)))
|
|
795
|
|
796 ;; The character position in the tags table is 0-origin.
|
|
797 ;; Convert it to a 1-origin Emacs character position.
|
|
798 (if startpos (setq startpos (1+ startpos)))
|
|
799 ;; If no char pos was given, try the given line number.
|
|
800 (or startpos
|
|
801 (if line
|
|
802 (setq startpos (progn (goto-line line)
|
|
803 (point)))))
|
|
804 (or startpos
|
|
805 (setq startpos (point-min)))
|
|
806 ;; First see if the tag is right at the specified location.
|
|
807 (goto-char startpos)
|
|
808 (setq found (looking-at pat))
|
|
809 (while (and (not found)
|
|
810 (progn
|
|
811 (goto-char (- startpos offset))
|
|
812 (not (bobp))))
|
|
813 (setq found
|
|
814 (re-search-forward pat (+ startpos offset) t)
|
|
815 offset (* 5 offset))) ; expand search window
|
|
816 ;; Finally, try finding it anywhere in the buffer.
|
|
817 (or found
|
|
818 (re-search-forward pat nil t)
|
|
819 (error "Rerun etags: `%s' not found in %s"
|
|
820 pat file))))
|
|
821 ;; Position point at the right place
|
|
822 ;; if the search string matched an extra Ctrl-m at the beginning.
|
|
823 (and (eq selective-display t)
|
|
824 (looking-at "\^m")
|
|
825 (forward-char 1))
|
|
826 (beginning-of-line)
|
|
827 (setq startpos (point))))
|
428
|
828 (cons buf startpos))))
|
|
829
|
|
830 ;;;###autoload
|
442
|
831 (defun find-tag-at-point (tagname &optional other-window)
|
|
832 "*Find tag whose name contains TAGNAME.
|
|
833 Identical to `find-tag' but does not prompt for tag when called interactively;
|
|
834 instead, uses tag around or before point."
|
|
835 (interactive (if current-prefix-arg
|
|
836 '(nil nil)
|
|
837 (list (find-tag-default) nil)))
|
|
838 (find-tag tagname other-window))
|
|
839
|
|
840 ;;;###autoload
|
428
|
841 (defun find-tag (tagname &optional other-window)
|
|
842 "*Find tag whose name contains TAGNAME.
|
|
843 Selects the buffer that the tag is contained in
|
|
844 and puts point at its definition.
|
|
845 If TAGNAME is a null string, the expression in the buffer
|
|
846 around or before point is used as the tag name.
|
|
847 If called interactively with a numeric argument, searches for the next tag
|
|
848 in the tag table that matches the tagname used in the previous find-tag.
|
|
849 If second arg OTHER-WINDOW is non-nil, uses another window to display
|
|
850 the tag.
|
|
851
|
|
852 This version of this function supports multiple active tags tables,
|
|
853 and completion.
|
|
854
|
|
855 Variables of note:
|
|
856
|
|
857 tag-table-alist controls which tables apply to which buffers
|
|
858 tags-file-name a default tags table
|
|
859 tags-build-completion-table controls completion behavior
|
|
860 buffer-tag-table another way of specifying a buffer-local table
|
|
861 make-tags-files-invisible whether tags tables should be very hidden
|
|
862 tag-mark-stack-max how many tags-based hops to remember"
|
|
863 (interactive (if current-prefix-arg
|
|
864 '(nil nil)
|
|
865 (list (find-tag-tag "Find tag: ") nil)))
|
|
866 (let* ((local-find-tag-hook find-tag-hook)
|
|
867 (next (null tagname))
|
|
868 (result (find-tag-internal tagname))
|
|
869 (tag-buf (car result))
|
|
870 (tag-point (cdr result)))
|
|
871 ;; Push old position on the tags mark stack.
|
|
872 (if (or (not next)
|
|
873 (not (memq last-command
|
|
874 '(find-tag find-tag-other-window tags-loop-continue))))
|
|
875 (push-tag-mark))
|
|
876 (if other-window
|
442
|
877 (pop-to-buffer tag-buf t)
|
428
|
878 (switch-to-buffer tag-buf))
|
|
879 (widen)
|
|
880 (push-mark)
|
|
881 (goto-char tag-point)
|
|
882 (if find-tag-hook
|
|
883 (run-hooks 'find-tag-hook)
|
|
884 (if local-find-tag-hook
|
|
885 (run-hooks 'local-find-tag-hook))))
|
|
886 (setq tags-loop-scan (list 'find-tag nil nil)
|
|
887 tags-loop-operate nil)
|
|
888 ;; Return t in case used as the tags-loop-scan.
|
|
889 t)
|
|
890
|
|
891 ;;;###autoload
|
|
892 (defun find-tag-other-window (tagname &optional next)
|
442
|
893 "*Find tag whose name contains TAGNAME, in another window.
|
428
|
894 Selects the buffer that the tag is contained in in another window
|
|
895 and puts point at its definition.
|
|
896 If TAGNAME is a null string, the expression in the buffer
|
|
897 around or before point is used as the tag name.
|
|
898 If second arg NEXT is non-nil (interactively, with prefix arg),
|
|
899 searches for the next tag in the tag table
|
|
900 that matches the tagname used in the previous find-tag.
|
|
901
|
|
902 This version of this function supports multiple active tags tables,
|
|
903 and completion.
|
|
904
|
|
905 Variables of note:
|
|
906
|
|
907 tag-table-alist controls which tables apply to which buffers
|
|
908 tags-file-name a default tags table
|
|
909 tags-build-completion-table controls completion behavior
|
|
910 buffer-tag-table another way of specifying a buffer-local table
|
|
911 make-tags-files-invisible whether tags tables should be very hidden
|
|
912 tag-mark-stack-max how many tags-based hops to remember"
|
|
913 (interactive (if current-prefix-arg
|
|
914 '(nil t)
|
|
915 (list (find-tag-tag "Find tag other window: "))))
|
|
916 (if next
|
|
917 (find-tag nil t)
|
|
918 (find-tag tagname t)))
|
|
919
|
|
920
|
|
921 ;; Completion on tags in the buffer.
|
|
922
|
|
923 (defun complete-symbol (&optional table predicate prettify)
|
|
924 (let* ((end (point))
|
|
925 (beg (save-excursion
|
|
926 (backward-sexp 1)
|
|
927 ;;(while (= (char-syntax (following-char)) ?\')
|
|
928 ;; (forward-char 1))
|
|
929 (skip-syntax-forward "'")
|
|
930 (point)))
|
|
931 (pattern (buffer-substring beg end))
|
|
932 (table (or table obarray))
|
|
933 (completion (try-completion pattern table predicate)))
|
|
934 (cond ((eq completion t))
|
|
935 ((null completion)
|
|
936 (error "Can't find completion for \"%s\"" pattern))
|
|
937 ((not (string-equal pattern completion))
|
|
938 (delete-region beg end)
|
|
939 (insert completion))
|
|
940 (t
|
|
941 (message "Making completion list...")
|
|
942 (let ((list (all-completions pattern table predicate)))
|
|
943 (if prettify
|
|
944 (setq list (funcall prettify list)))
|
|
945 (with-output-to-temp-buffer "*Help*"
|
|
946 (display-completion-list list)))
|
|
947 (message "Making completion list...%s" "done")))))
|
|
948
|
|
949 ;;;###autoload
|
|
950 (defun tag-complete-symbol ()
|
|
951 "The function used to do tags-completion (using 'tag-completion-predicate)."
|
|
952 (interactive)
|
|
953 (let* ((buffer-tag-table-list (buffer-tag-table-symbol-list))
|
|
954 tag-symbol-tables)
|
|
955 (complete-symbol tag-completion-table 'tag-completion-predicate)))
|
|
956
|
|
957
|
|
958 ;; Applying a command to files mentioned in tag tables
|
|
959
|
|
960 (defvar next-file-list nil
|
|
961 "List of files for next-file to process.")
|
|
962
|
|
963 ;;;###autoload
|
|
964 (defun next-file (&optional initialize novisit)
|
|
965 "Select next file among files in current tag table(s).
|
|
966
|
|
967 A first argument of t (prefix arg, if interactive) initializes to the
|
|
968 beginning of the list of files in the (first) tags table. If the argument
|
|
969 is neither nil nor t, it is evalled to initialize the list of files.
|
|
970
|
|
971 Non-nil second argument NOVISIT means use a temporary buffer
|
|
972 to save time and avoid uninteresting warnings.
|
|
973
|
|
974 Value is nil if the file was already visited;
|
|
975 if the file was newly read in, the value is the filename."
|
|
976 (interactive "P")
|
|
977 (cond ((not initialize)
|
|
978 ;; Not the first run.
|
|
979 )
|
|
980 ((eq initialize t)
|
|
981 ;; Initialize the list from the tags table.
|
|
982 (setq next-file-list (buffer-tag-table-files)))
|
|
983 (t
|
|
984 ;; Initialize the list by evalling the argument.
|
|
985 (setq next-file-list (eval initialize))))
|
|
986 (when (null next-file-list)
|
|
987 (and novisit
|
|
988 (get-buffer " *next-file*")
|
|
989 (kill-buffer " *next-file*"))
|
|
990 (error "All files processed"))
|
|
991 (let* ((file (car next-file-list))
|
|
992 (buf (get-file-buffer file))
|
|
993 (new (not buf)))
|
|
994 (pop next-file-list)
|
|
995
|
|
996 (if (not (and new novisit))
|
|
997 (switch-to-buffer (find-file-noselect file novisit) t)
|
|
998 ;; Like find-file, but avoids random junk.
|
|
999 (set-buffer (get-buffer-create " *next-file*"))
|
|
1000 (kill-all-local-variables)
|
|
1001 (erase-buffer)
|
|
1002 (insert-file-contents file nil))
|
|
1003 (widen)
|
|
1004 (when (> (point) (point-min))
|
|
1005 (push-mark nil t)
|
|
1006 (goto-char (point-min)))
|
|
1007 (and new file)))
|
|
1008
|
|
1009 ;;;###autoload
|
|
1010 (defun tags-loop-continue (&optional first-time)
|
|
1011 "Continue last \\[tags-search] or \\[tags-query-replace] command.
|
|
1012 Used noninteractively with non-nil argument to begin such a command (the
|
|
1013 argument is passed to `next-file', which see).
|
|
1014 Two variables control the processing we do on each file:
|
|
1015 the value of `tags-loop-scan' is a form to be executed on each file
|
|
1016 to see if it is interesting (it returns non-nil if so)
|
|
1017 and `tags-loop-operate' is a form to execute to operate on an interesting file
|
|
1018 If the latter returns non-nil, we exit; otherwise we scan the next file."
|
|
1019 (interactive)
|
|
1020 (let ((messaged nil)
|
|
1021 (more-files-p t)
|
|
1022 new)
|
|
1023 (while more-files-p
|
|
1024 ;; Scan files quickly for the first or next interesting one.
|
|
1025 (while (or first-time
|
|
1026 (save-restriction
|
|
1027 (widen)
|
|
1028 (not (eval tags-loop-scan))))
|
|
1029 (setq new (next-file first-time
|
|
1030 tags-search-nuke-uninteresting-buffers))
|
|
1031 ;; If NEW is non-nil, we got a temp buffer,
|
|
1032 ;; and NEW is the file name.
|
|
1033 (if (or messaged
|
|
1034 (and (not first-time)
|
|
1035 (> (device-baud-rate) search-slow-speed)
|
|
1036 (setq messaged t)))
|
|
1037 (lmessage 'progress
|
|
1038 "Scanning file %s..." (or new buffer-file-name)))
|
|
1039 (setq first-time nil)
|
|
1040 (goto-char (point-min)))
|
|
1041
|
|
1042 ;; If we visited it in a temp buffer, visit it now for real.
|
|
1043 (if (and new tags-search-nuke-uninteresting-buffers)
|
|
1044 (let ((pos (point)))
|
|
1045 (erase-buffer)
|
|
1046 (set-buffer (find-file-noselect new))
|
|
1047 (widen)
|
|
1048 (goto-char pos)))
|
|
1049
|
|
1050 (switch-to-buffer (current-buffer))
|
|
1051
|
|
1052 ;; Now operate on the file.
|
|
1053 ;; If value is non-nil, continue to scan the next file.
|
|
1054 (setq more-files-p (eval tags-loop-operate)))
|
|
1055 (and messaged
|
|
1056 (null tags-loop-operate)
|
|
1057 (message "Scanning file %s...found" buffer-file-name))))
|
|
1058
|
|
1059
|
|
1060 ;;;###autoload
|
|
1061 (defun tags-search (regexp &optional file-list-form)
|
|
1062 "Search through all files listed in tags table for match for REGEXP.
|
|
1063 Stops when a match is found.
|
|
1064 To continue searching for next match, use command \\[tags-loop-continue].
|
|
1065
|
|
1066 See documentation of variable `tag-table-alist'."
|
|
1067 (interactive "sTags search (regexp): ")
|
|
1068 (if (and (equal regexp "")
|
|
1069 (eq (car tags-loop-scan) 'with-search-caps-disable-folding)
|
|
1070 (null tags-loop-operate))
|
|
1071 ;; Continue last tags-search as if by `M-,'.
|
|
1072 (tags-loop-continue nil)
|
|
1073 (setq tags-loop-scan `(with-search-caps-disable-folding ,regexp t
|
|
1074 (re-search-forward ,regexp nil t))
|
|
1075 tags-loop-operate nil)
|
|
1076 (tags-loop-continue (or file-list-form t))))
|
|
1077
|
|
1078 ;;;###autoload
|
|
1079 (defun tags-query-replace (from to &optional delimited file-list-form)
|
|
1080 "Query-replace-regexp FROM with TO through all files listed in tags table.
|
|
1081 Third arg DELIMITED (prefix arg) means replace only word-delimited matches.
|
|
1082 If you exit (\\[keyboard-quit] or ESC), you can resume the query-replace
|
|
1083 with the command \\[tags-loop-continue].
|
|
1084
|
|
1085 See documentation of variable `tag-table-alist'."
|
|
1086 (interactive
|
|
1087 "sTags query replace (regexp): \nsTags query replace %s by: \nP")
|
|
1088 (setq tags-loop-scan `(with-search-caps-disable-folding ,from t
|
|
1089 (if (re-search-forward ,from nil t)
|
|
1090 ;; When we find a match, move back
|
|
1091 ;; to the beginning of it so perform-replace
|
|
1092 ;; will see it.
|
|
1093 (progn (goto-char (match-beginning 0)) t)))
|
|
1094 tags-loop-operate (list 'perform-replace from to t t
|
|
1095 (not (null delimited))))
|
|
1096 (tags-loop-continue (or file-list-form t)))
|
|
1097
|
|
1098 ;; Miscellaneous
|
|
1099
|
|
1100 ;;;###autoload
|
|
1101 (defun list-tags (file)
|
|
1102 "Display list of tags in FILE."
|
|
1103 (interactive (list (read-file-name
|
|
1104 (if (buffer-file-name)
|
|
1105 (format "List tags (in file, %s by default): "
|
|
1106 (file-name-nondirectory (buffer-file-name)))
|
|
1107 "List tags (in file): ")
|
|
1108 nil (buffer-file-name) t)))
|
|
1109 (find-file-noselect file)
|
|
1110 (with-output-to-temp-buffer "*Tags List*"
|
|
1111 (princ "Tags in file ")
|
|
1112 (princ file)
|
|
1113 (terpri)
|
|
1114 (save-excursion
|
|
1115 (dolist (tags-file (with-current-buffer (get-file-buffer file)
|
|
1116 (buffer-tag-table-list)))
|
|
1117 ;; We don't want completions getting in the way.
|
|
1118 (let ((tags-build-completion-table nil))
|
|
1119 (set-buffer (get-tag-table-buffer tags-file)))
|
|
1120 (goto-char (point-min))
|
|
1121 (when
|
|
1122 (search-forward (concat "\f\n" (file-name-nondirectory file) ",")
|
|
1123 nil t)
|
|
1124 (forward-line 1)
|
|
1125 (while (not (or (eobp) (looking-at "\f")))
|
|
1126 (princ (buffer-substring (point)
|
|
1127 (progn (skip-chars-forward "^\C-?")
|
|
1128 (point))))
|
|
1129 (terpri)
|
|
1130 (forward-line 1)))))))
|
|
1131
|
|
1132 ;;;###autoload
|
|
1133 (defun tags-apropos (string)
|
|
1134 "Display list of all tags in tag table REGEXP matches."
|
|
1135 (interactive "sTag apropos (regexp): ")
|
|
1136 (with-output-to-temp-buffer "*Tags List*"
|
|
1137 (princ "Tags matching regexp ")
|
|
1138 (prin1 string)
|
|
1139 (terpri)
|
|
1140 (save-excursion
|
|
1141 (visit-tags-table-buffer)
|
|
1142 (goto-char 1)
|
|
1143 (while (re-search-forward string nil t)
|
|
1144 (beginning-of-line)
|
|
1145 (princ (buffer-substring (point)
|
|
1146 (progn (skip-chars-forward "^\C-?")
|
|
1147 (point))))
|
|
1148 (terpri)
|
|
1149 (forward-line 1)))))
|
|
1150
|
|
1151 ;; #### copied from tags.el. This function is *very* big in FSF.
|
|
1152 (defun visit-tags-table-buffer ()
|
|
1153 "Select the buffer containing the current tag table."
|
|
1154 (or tags-file-name
|
|
1155 (call-interactively 'visit-tags-table))
|
|
1156 (set-buffer (or (get-file-buffer tags-file-name)
|
|
1157 (progn
|
|
1158 (setq tag-table-files nil)
|
|
1159 (find-file-noselect tags-file-name))))
|
|
1160 (or (verify-visited-file-modtime (get-file-buffer tags-file-name))
|
|
1161 (cond ((yes-or-no-p "Tags file has changed, read new contents? ")
|
|
1162 (revert-buffer t t)
|
|
1163 (setq tag-table-files nil))))
|
|
1164 (or (eq (char-after 1) ?\^L)
|
|
1165 (error "File %s not a valid tag table" tags-file-name)))
|
|
1166
|
|
1167
|
|
1168 ;; Sample uses of find-tag-hook and find-tag-default-hook
|
|
1169
|
|
1170 ;; This is wrong. We should either make this behavior default and
|
|
1171 ;; back it up, or not use it at all. For now, I've commented it out.
|
|
1172 ;; --hniksic
|
|
1173
|
|
1174 ;; Example buffer-local tag finding
|
|
1175
|
|
1176 ;(add-hook 'emacs-lisp-mode-hook 'setup-emacs-lisp-default-tag-hook)
|
|
1177
|
|
1178 ;(defun setup-emacs-lisp-default-tag-hook ()
|
|
1179 ; (cond ((eq major-mode 'emacs-lisp-mode)
|
|
1180 ; (make-variable-buffer-local 'find-tag-default-hook)
|
|
1181 ; (setq find-tag-default-hook 'emacs-lisp-default-tag))))
|
|
1182 ;;; Run it once immediately
|
|
1183 ;(setup-emacs-lisp-default-tag-hook)
|
|
1184 ;(when (get-buffer "*scratch*")
|
|
1185 ; (with-current-buffer "*scratch*"
|
|
1186 ; (setup-emacs-lisp-default-tag-hook)))
|
|
1187
|
|
1188 ;(defun emacs-lisp-default-tag ()
|
|
1189 ; "Function to return a default tag for Emacs-Lisp mode."
|
|
1190 ; (let ((tag (or (variable-at-point)
|
|
1191 ; (function-at-point))))
|
|
1192 ; (if tag (symbol-name tag))))
|
|
1193
|
|
1194
|
|
1195 ;; Display short info on tag in minibuffer
|
|
1196
|
|
1197 ;; Don't pollute `M-?' -- we may need it for more important stuff. --hniksic
|
|
1198 ;(if (null (lookup-key esc-map "?"))
|
|
1199 ; (define-key esc-map "?" 'display-tag-info))
|
|
1200
|
|
1201 (defun display-tag-info (tagname)
|
|
1202 "Prints a description of the first tag matching TAGNAME in the echo area.
|
|
1203 If this is an elisp function, prints something like \"(defun foo (x y z)\".
|
|
1204 That is, is prints the first line of the definition of the form.
|
|
1205 If this is a C-defined elisp function, it does something more clever."
|
|
1206 (interactive (if current-prefix-arg
|
|
1207 '(nil)
|
|
1208 (list (find-tag-tag "Display tag info: "))))
|
|
1209 (let* ((results (find-tag-internal tagname))
|
|
1210 (tag-buf (car results))
|
|
1211 (tag-point (cdr results))
|
|
1212 info lname min max fname args)
|
|
1213 (with-current-buffer tag-buf
|
|
1214 (save-excursion
|
|
1215 (save-restriction
|
|
1216 (widen)
|
|
1217 (goto-char tag-point)
|
|
1218 (cond ((let ((case-fold-search nil))
|
|
1219 (looking-at "^DEFUN[ \t]"))
|
|
1220 (forward-sexp 1)
|
|
1221 (down-list 1)
|
|
1222 (setq lname (read (current-buffer))
|
|
1223 fname (buffer-substring
|
|
1224 (progn (forward-sexp 1) (point))
|
|
1225 (progn (backward-sexp 1) (point)))
|
|
1226 min (buffer-substring
|
|
1227 (progn (forward-sexp 3) (point))
|
|
1228 (progn (backward-sexp 1) (point)))
|
|
1229 max (buffer-substring
|
|
1230 (progn (forward-sexp 2) (point))
|
|
1231 (progn (backward-sexp 1) (point))))
|
|
1232 (backward-up-list 1)
|
|
1233 (setq args (buffer-substring
|
|
1234 (progn (forward-sexp 2) (point))
|
|
1235 (progn (backward-sexp 1) (point))))
|
|
1236 (setq info (format "Elisp: %s, C: %s %s, #args: %s"
|
|
1237 lname
|
|
1238 fname args
|
|
1239 (if (string-equal min max)
|
|
1240 min
|
|
1241 (format "from %s to %s" min max)))))
|
|
1242 (t
|
|
1243 (setq info
|
|
1244 (buffer-substring
|
|
1245 (progn (beginning-of-line) (point))
|
|
1246 (progn (end-of-line) (point)))))))))
|
|
1247 (message "%s" info))
|
|
1248 (setq tags-loop-scan '(display-tag-info nil)
|
|
1249 tags-loop-operate nil)
|
|
1250 ;; Always return non-nil
|
|
1251 t)
|
|
1252
|
|
1253
|
|
1254 ;; Tag mark stack.
|
|
1255
|
|
1256 (defvar tag-mark-stack1 nil)
|
|
1257 (defvar tag-mark-stack2 nil)
|
|
1258
|
|
1259 (defcustom tag-mark-stack-max 16
|
|
1260 "*The maximum number of elements kept on the mark-stack used
|
|
1261 by tags-search. See also the commands `\\[push-tag-mark]' and
|
|
1262 and `\\[pop-tag-mark]'."
|
|
1263 :type 'integer
|
|
1264 :group 'etags)
|
|
1265
|
|
1266 (defun push-mark-on-stack (stack-symbol &optional max-size)
|
|
1267 (let ((stack (symbol-value stack-symbol)))
|
|
1268 (push (point-marker) stack)
|
|
1269 (cond ((and max-size
|
|
1270 (> (length stack) max-size))
|
|
1271 (set-marker (car (nthcdr max-size stack)) nil)
|
|
1272 (setcdr (nthcdr (1- max-size) stack) nil)))
|
|
1273 (set stack-symbol stack)))
|
|
1274
|
|
1275 (defun pop-mark-from-stack (stack-symbol1 stack-symbol2 &optional max-size)
|
|
1276 (let* ((stack (or (symbol-value stack-symbol1)
|
|
1277 (error "No more tag marks on stack")))
|
|
1278 (marker (car stack))
|
|
1279 (m-buf (marker-buffer marker)))
|
|
1280 (set stack-symbol1 (cdr stack))
|
|
1281 (or m-buf
|
|
1282 (error "Marker has no buffer"))
|
|
1283 (or (buffer-live-p m-buf)
|
|
1284 (error "Buffer has been killed"))
|
|
1285 (push-mark-on-stack stack-symbol2 max-size)
|
|
1286 (switch-to-buffer m-buf)
|
|
1287 (widen)
|
|
1288 (goto-char marker)))
|
|
1289
|
|
1290 (defun push-tag-mark ()
|
|
1291 (push-mark-on-stack 'tag-mark-stack1 tag-mark-stack-max))
|
|
1292
|
|
1293 ;;;###autoload (define-key esc-map "*" 'pop-tag-mark)
|
|
1294
|
|
1295 ;;;###autoload
|
|
1296 (defun pop-tag-mark (arg)
|
|
1297 "Go to last tag position.
|
|
1298 `find-tag' maintains a mark-stack seperate from the \\[set-mark-command] mark-stack.
|
|
1299 This function pops (and moves to) the tag at the top of this stack."
|
|
1300 (interactive "P")
|
|
1301 (if (not arg)
|
|
1302 (pop-mark-from-stack
|
|
1303 'tag-mark-stack1 'tag-mark-stack2 tag-mark-stack-max)
|
|
1304 (pop-mark-from-stack
|
|
1305 'tag-mark-stack2 'tag-mark-stack1 tag-mark-stack-max)))
|
|
1306
|
|
1307
|
|
1308 (provide 'etags)
|
|
1309 (provide 'tags)
|
|
1310
|
|
1311 ;;; etags.el ends here
|