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