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