comparison lisp/hyperbole/hmouse-tag.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 4103f0995bd7
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
1 ;;!emacs
2 ;;
3 ;; FILE: hmouse-tag.el
4 ;; SUMMARY: Smart Key support of programming language tags location.
5 ;; USAGE: GNU Emacs Lisp Library
6 ;; KEYWORDS: c, hypermedia, mouse, oop, tools
7 ;;
8 ;; AUTHOR: Bob Weiner
9 ;; ORG: Brown U.
10 ;;
11 ;; ORIG-DATE: 24-Aug-91
12 ;; LAST-MOD: 11-Sep-95 at 18:19:48 by Bob Weiner
13 ;;
14 ;; This file is part of Hyperbole.
15 ;; Available for use and distribution under the same terms as GNU Emacs.
16 ;;
17 ;; Copyright (C) 1991-1995, Free Software Foundation, Inc.
18 ;; Developed with support from Motorola Inc.
19 ;;
20 ;; DESCRIPTION:
21 ;;
22 ;; Supports C, C++, Objective-C, Lisp, Fortran, and Assembly.
23 ;; See the GNU Emacs manual for information on how to create a TAGS file
24 ;; from the `etags' program.
25 ;; Does not support the `ctags' tags file format.
26 ;;
27 ;; YOU MUST APPROPRIATELY SET THE PUBLIC VARIABLES BELOW BEFORE USE.
28 ;;
29 ;; DESCRIP-END.
30
31 ;;; ************************************************************************
32 ;;; Other required Elisp libraries
33 ;;; ************************************************************************
34
35 (if (cond ((or (featurep 'etags) (featurep 'tags))
36 nil)
37 ((or hyperb:lemacs-p hyperb:emacs19-p)
38 ;; Force use of .elc file here since otherwise the bin/etags
39 ;; executable might be found in a user's load-path by the load
40 ;; command.
41 (or (load "etags.elc" t nil t)
42 (load "tags-fix" t)))
43 ((load "tags" t)))
44 (provide 'tags))
45
46 ;;; ************************************************************************
47 ;;; Public variables
48 ;;; ************************************************************************
49
50 (defvar smart-asm-include-dirs nil
51 "*Ordered list of directories to search for assembly language include files.
52 Each directory must end with a directory separator.")
53
54 (defconst smart-asm-include-regexp
55 "[ \t*#|;]*\\(include\\|lib\\)[ \t]+\\([^ \t\n\^M]+\\)"
56 "Regexp to match to assembly language include file lines.
57 Include keyword matched is grouping 1. File name is grouping 2 but may be
58 missing its suffix, so add \".ins\" or \".inc\" if need be.
59 Examples include:
60 INCLUDE GLOBALS
61 should jump to file \"globals.ins\"
62 lib conditionals_equ.inc
63 should include \"conditionals_equ.inc\"")
64
65 (defvar smart-c-cpp-include-dirs '("/usr/include/")
66 "*Ordered list of include directories by default searched by C/C++ preprocessor.
67 Each directory must end with a directory separator. See also
68 'smart-c-include-dirs'.")
69
70 (defvar smart-c-include-dirs nil
71 "*Ordered list of directories to search for C/C++ include files.
72 Each directory must end with a directory separator. Directories normally
73 searched by the C/C++ pre-processor should be set instead in
74 'smart-c-cpp-include-dirs'.")
75
76 (defvar smart-c-use-lib-man nil
77 "When non-nil makes 'smart-c' and 'smart-c++' display man pages for recognized lib symbols.
78 When nil, 'smart-c' and 'smart-c++' look up only symbols defined in an etags
79 TAGS file.
80
81 Create the file ~/.CLIBS-LIST and populate it with the full pathnames (one per
82 line) of all of the C/C++ libraries whose symbols you want to match against.
83 Your MANPATH environment variable must include paths for the man pages of
84 these libraries also.
85
86 Your smart-clib-sym executable script must output a 1 if a symbol is from a
87 C/C++ library listed in ~/.CLIBS-LIST or 0 if not! Otherwise, don't set this
88 variable to t.")
89
90 (defconst smart-c-include-regexp
91 "[ \t/*]*#[ \t]*\\(include\\|import\\)[ \t]+\\([\"<]\\)\\([^\">]+\\)[\">]"
92 "Regexp to match to C, C++, or Objective-C include file lines.
93 Include keyword matched is grouping 1. Type of include, user-specified via
94 double quote, or system-related starting with '<' is given by grouping 2.
95 File name is grouping 3.")
96
97 (defvar smart-emacs-tags-file nil
98 "*Full path name of etags file for GNU Emacs source.")
99
100 ;;; ************************************************************************
101 ;;; Public functions
102 ;;; ************************************************************************
103
104 (defun smart-asm (&optional identifier next)
105 "Jumps to the definition of optional assembly IDENTIFIER or the one at point.
106 Optional second arg NEXT means jump to next matching assembly tag.
107
108 It assumes that its caller has already checked that the key was pressed in an
109 appropriate buffer and has moved the cursor to the selected buffer.
110
111 If:
112 (1) on an include statement, the include file is displayed;
113 Look for include file in directory list 'smart-asm-include-dirs'.
114 (2) on an identifier, the identifier definition is displayed,
115 assuming the identifier is found within an 'etags' generated tag file
116 in the current directory or any of its ancestor directories."
117
118 (interactive)
119 (or
120 (if identifier nil (smart-asm-include-file))
121 (let ((tag (or identifier (smart-asm-at-tag-p))))
122 ;; Set free variable tags-file-name so that next 'find-tag' command uses
123 ;; whatever tags file is set here.
124 (setq tags-file-name (smart-tags-file buffer-file-name))
125 (message "Looking for '%s' in '%s'..." tag tags-file-name)
126 (condition-case ()
127 (progn
128 (funcall (if (br-in-browser)
129 'find-tag 'find-tag-other-window)
130 tag next)
131 (message "Found definition for '%s'." tag))
132 (error (message "'%s' not found in '%s'." tag tags-file-name)
133 (beep))))))
134
135 ;;;###autoload
136 (defun smart-asm-at-tag-p ()
137 "Return assembly tag name that point is within, else nil."
138 (let* ((identifier-chars "_.$a-zA-Z0-9")
139 (identifier (concat "[_.$a-zA-Z][" identifier-chars "]*")))
140 (save-excursion
141 (skip-chars-backward identifier-chars)
142 (if (looking-at identifier)
143 (buffer-substring (point) (match-end 0))))))
144
145 (defun smart-asm-include-file ()
146 "If point is on an include file line, tries to display file.
147 Returns non-nil iff on an include file line, even if file is not found.
148 Look for include file in 'smart-asm-include-dirs' and add suffix \".ins\" or
149 \".inc\" to filename if it lacks a suffix."
150 (let ((opoint (point)))
151 ;; Some assemblers utilize the C preprocessor, so try that first.
152 (cond ((smart-c-include-file))
153 ((progn (beginning-of-line)
154 (looking-at smart-asm-include-regexp))
155 (let ((file (buffer-substring (match-beginning 2) (match-end 2)))
156 (path)
157 (dir-list smart-asm-include-dirs))
158 (goto-char opoint)
159 (setq dir-list (cons (file-name-directory buffer-file-name)
160 dir-list))
161 (if (string-match "\\." file)
162 (setq file (regexp-quote file))
163 (setq file (concat (regexp-quote file) "\\.in[sc]$")))
164 (while dir-list
165 (setq dir-list
166 (if (setq path (car (directory-files
167 (car dir-list) t file)))
168 nil
169 (cdr dir-list))))
170 ;;
171 ;; If path exists, display file
172 ;;
173 (if path
174 (if (and (file-readable-p path)
175 (progn
176 (if (br-in-browser)
177 (find-file path)
178 (find-file-other-window path))
179 (cond ((featurep 'asm-mode) t)
180 ((load "asm-mode" nil 'nomessage)
181 (provide 'asm-mode))
182 (t
183 (beep)
184 (message
185 "(smart-asm-include-file): asm-mode undefined.")
186 nil
187 ))))
188 nil
189 (beep)
190 (message "(smart-asm-include-file): '%s' unreadable." path))
191 (beep)
192 (message "(smart-asm-include-file): '%s' not found." file))
193 path))
194 ;; not on an include file line
195 (t (goto-char opoint)
196 nil))))
197
198
199 (defun smart-c (&optional identifier next)
200 "Jumps to the definition of optional C IDENTIFIER or the one at point.
201 Optional second arg NEXT means jump to next matching C tag.
202
203 It assumes that its caller has already checked that the key was pressed in an
204 appropriate buffer and has moved the cursor to the selected buffer.
205
206 If:
207 (1) on a '#include' statement, the include file is displayed;
208 Look for include file in directory lists 'smart-c-cpp-include-dirs'
209 and 'smart-c-include-dirs'.
210 (2) on a C identifier, the identifier definition is displayed,
211 assuming the identifier is found within an 'etags' generated tag file
212 in the current directory or any of its ancestor directories.
213 (3) if 'smart-c-use-lib-man' is non-nil, the C identifier is
214 recognized as a library symbol, and a man page is found for the
215 identifier, then the man page is displayed."
216
217 (interactive)
218 (or
219 (if identifier nil (smart-c-include-file))
220 (let ((tag (or identifier (smart-c-at-tag-p))))
221 ;; Set free variable tags-file-name so that next 'find-tag' command uses
222 ;; whatever tags file is set here.
223 (setq tags-file-name (smart-tags-file buffer-file-name))
224 (message "Looking for '%s' in '%s'..." tag tags-file-name)
225 (condition-case ()
226 (progn
227 (funcall (if (br-in-browser)
228 'find-tag 'find-tag-other-window)
229 tag next)
230 (message "Found definition for '%s'." tag))
231 (error
232 (if (not smart-c-use-lib-man)
233 (progn (message "'%s' not found in '%s'." tag tags-file-name)
234 (beep))
235 (message "Checking if '%s' is a C library function..." tag)
236 (if (smart-library-symbol tag)
237 (progn (message "Displaying C library man page for '%s'." tag)
238 (manual-entry tag))
239 (message "'%s' not found in '%s' or C libraries."
240 tag tags-file-name)
241 (beep))))))))
242
243 ;;;###autoload
244 (defun smart-c-at-tag-p ()
245 "Return C tag name that point is within, else nil."
246 (let* ((identifier-chars "_a-zA-Z0-9")
247 (identifier (concat "[_a-zA-Z][" identifier-chars "]*")))
248 (save-excursion
249 (skip-chars-backward identifier-chars)
250 (if (looking-at identifier)
251 (buffer-substring (point) (match-end 0))))))
252
253 (defun smart-c-include-file ()
254 "If point is on an include file line, tries to display file.
255 Returns non-nil iff on an include file line, even if file is not found.
256 Look for include file in 'smart-c-cpp-include-dirs' and in directory list
257 'smart-c-include-dirs'."
258 (let ((opoint (point)))
259 (beginning-of-line)
260 (if (looking-at smart-c-include-regexp)
261 (let ((incl-type (string-to-char
262 (buffer-substring (match-beginning 2)
263 (1+ (match-beginning 2)))))
264 (file (buffer-substring (match-beginning 3) (match-end 3)))
265 (path)
266 (dir-list smart-c-include-dirs)
267 (found))
268 (goto-char opoint)
269 (setq dir-list (if (= incl-type ?<)
270 (append dir-list smart-c-cpp-include-dirs)
271 (cons (file-name-directory buffer-file-name)
272 dir-list)))
273 (while dir-list
274 (setq path (expand-file-name file (car dir-list))
275 dir-list (if (setq found (file-exists-p path))
276 nil
277 (cdr dir-list))))
278 ;;
279 ;; If found, display file
280 ;;
281 (if found
282 (if (and (file-readable-p path)
283 (progn
284 (if (br-in-browser)
285 (find-file path)
286 (find-file-other-window path))
287 (cond ((or (featurep 'cc-mode)
288 (featurep 'c-mode))
289 t)
290 ((or (load "cc-mode" 'missing-ok 'nomessage)
291 (load "c-mode" 'missing-ok 'nomessage))
292 (provide 'c-mode))
293 (t
294 (beep)
295 (message
296 "(smart-c-include-file): c-mode undefined.")
297 nil
298 ))))
299 nil
300 (beep)
301 (message "(smart-c-include-file): '%s' unreadable." path))
302 (beep)
303 (message "(smart-c-include-file): '%s' not found." file))
304 path)
305 (goto-char opoint)
306 nil)))
307
308
309 ;;;###autoload
310 (defun smart-c++ (&optional identifier next)
311 "Jumps to the definition of optional C++ IDENTIFIER or the one at point.
312 Optional second arg NEXT means jump to next matching C++ tag.
313
314 It assumes that its caller has already checked that the key was pressed in an
315 appropriate buffer and has moved the cursor to the selected buffer.
316
317 If:
318 (1) on a '#include' statement, the include file is displayed;
319 Look for include file in directory lists 'smart-c-cpp-include-dirs'
320 and 'smart-c-include-dirs'.
321 (2) on a C++ identifier, the identifier definition is displayed,
322 assuming the identifier is found within an 'etags' generated tag file
323 in the current directory or any of its ancestor directories.
324 (3) if 'smart-c-use-lib-man' is non-nil, the C++ identifier is
325 recognized as a library symbol, and a man page is found for the
326 identifier, then the man page is displayed."
327
328 (interactive)
329 (or
330 (if identifier nil (smart-c-include-file))
331 (let ((tag (or identifier (smart-c++-at-tag-p))))
332 ;; Set free variable tags-file-name so that next 'find-tag' command uses
333 ;; whatever tags file is set here.
334 (setq tags-file-name (smart-tags-file buffer-file-name))
335 (message "Looking for '%s' in '%s'..." tag tags-file-name)
336 (condition-case ()
337 (progn
338 (funcall (if (br-in-browser)
339 'find-tag 'find-tag-other-window)
340 tag next)
341 (message "Found definition for '%s'." tag))
342 (error
343 (if (not smart-c-use-lib-man)
344 (progn (message "'%s' not found in '%s'." tag tags-file-name)
345 (beep))
346 (message "Checking if '%s' is a C++ library function..." tag)
347 (if (smart-library-symbol tag)
348 (progn (message "Displaying C++ library man page for '%s'." tag)
349 (manual-entry tag))
350 (message "'%s' not found in '%s' or C++ libraries."
351 tag tags-file-name)
352 (beep))))))))
353
354 ;;; The following should be called only if the OO-Browser is available.
355 ;;;###autoload
356 (defun smart-c++-oobr (&optional junk)
357 "Jumps to the definition of selected C++ construct via OO-Browser support.
358 Optional JUNK is ignored. Does nothing if the OO-Browser is not available.
359
360 It assumes that its caller has already checked that the key was pressed in an
361 appropriate buffer and has moved the cursor to the selected buffer.
362
363 If key is pressed:
364 (1) on a '#include' statement, the include file is displayed;
365 Look for include file in directory lists 'smart-c-cpp-include-dirs'
366 and 'smart-c-include-dirs'.
367 (2) within a method declaration, its definition is displayed;
368 (3) on a class name, the class definition is shown.
369
370 (2) and (3) require that an OO-Browser Environment has been loaded with
371 the {M-x br-env-load RTN} command."
372
373 (interactive)
374 (c++-to-definition 'other-win))
375
376 (defun smart-c++-at-tag-p ()
377 "Return C++ tag name that point is within, else nil."
378 (let* ((identifier-chars "_:~<>a-zA-Z0-9")
379 (identifier (concat "\\([_~:<a-zA-Z][" identifier-chars "]*"
380 "[ \t]*[^]) \t:;.,?~{}][^[( \t:;.,~^!|?{}]?[=*]?\\)[ \t\n]*(")))
381 (save-excursion
382 (skip-chars-backward identifier-chars)
383 (if (looking-at identifier)
384 (buffer-substring (point) (match-end 1))))))
385
386 (defun smart-emacs-lisp-mode-p ()
387 "Return t if in a mode which uses Emacs Lisp symbols."
388 (or (eq major-mode 'emacs-lisp-mode)
389 (eq major-mode 'lisp-interaction-mode)
390 (eq major-mode 'debugger-mode)
391 ;; Emacs Lisp symbols appear in Help buffers frequently.
392 (string-match "Help\\*$" (buffer-name))))
393
394 (defun smart-fortran (&optional identifier next)
395 "Jumps to the definition of optional Fortran IDENTIFIER or the one at point.
396 Optional second arg NEXT means jump to next matching Fortran tag.
397
398 It assumes that its caller has already checked that the key was pressed in an
399 appropriate buffer and has moved the cursor to the selected buffer.
400
401 If on a Fortran identifier, the identifier definition is displayed,
402 assuming the identifier is found within an 'etags' generated tag file
403 in the current directory or any of its ancestor directories."
404 (interactive)
405 (let ((tag (or identifier (smart-fortran-at-tag-p))))
406 ;; Set free variable tags-file-name so that next 'find-tag' command uses
407 ;; whatever tags file is set here.
408 (setq tags-file-name (smart-tags-file buffer-file-name))
409 (message "Looking for '%s' in '%s'..." tag tags-file-name)
410 (condition-case ()
411 (progn
412 (funcall (if (br-in-browser)
413 'find-tag 'find-tag-other-window)
414 tag next)
415 (message "Found definition for '%s'." tag))
416 (error
417 (message "'%s' not found in '%s'." tag tags-file-name)
418 (beep)))))
419
420 ;;;###autoload
421 (defun smart-fortran-at-tag-p ()
422 "Return Fortran tag name that point is within, else nil."
423 (let* ((identifier-chars "_a-zA-Z0-9")
424 (identifier (concat "[_a-zA-Z][" identifier-chars "]*")))
425 (save-excursion
426 (skip-chars-backward identifier-chars)
427 (if (looking-at identifier)
428 (buffer-substring (point) (match-end 0))))))
429
430 (defun smart-lisp (&optional next)
431 "Jumps to the definition of any selected Lisp construct.
432 If on an Emacs Lisp require, load, or autoload clause and 'find-library'
433 from load-library package by Hallvard Furuseth (hallvard@ifi.uio.no) has
434 been loaded, jumps to library source, if possible.
435
436 Otherwise, the construct must be found within an 'etags' generated tag file
437 in the current directory or any of its ancestor directories in order for its
438 definition to be located.
439
440 Optional NEXT means jump to next matching Lisp tag. When matching to an Emacs
441 Lisp tag using 'wtags' (Bob Weiner's personal modifications to 'etags'),
442 there is no next tag, so display documentation for current tag instead.
443
444 This command assumes that its caller has already checked that the key was
445 pressed in an appropriate buffer and has moved the cursor to the selected
446 buffer."
447
448 (interactive)
449 ;; Handle 'require', 'load', and 'autoload' clauses in Emacs Lisp.
450 (or (and (fboundp 'find-library)
451 (smart-emacs-lisp-mode-p)
452 (let ((req)
453 (opoint (point)))
454 (setq req (and (search-backward "\(" nil t)
455 (looking-at (concat
456 "(\\(require\\|load\\|autoload\\)"
457 "[ \t]+.*['\"]"
458 "\\([^][() \t\n\^M`'\"]+\\)"))))
459 (goto-char opoint)
460 (if req (progn
461 (setq req (buffer-substring (match-beginning 2)
462 (match-end 2)))
463 (pop-to-buffer nil t)
464 (find-library req)
465 t))))
466 (let ((tag (smart-lisp-at-tag-p)))
467 ;; Set free variable tags-file-name so that next 'find-tag' command
468 ;; uses whatever tags file is set here.
469 (setq tags-file-name (smart-tags-file default-directory))
470 ;; This part only works properly for Emacs Lisp, so is conditionalized.
471 (if (and next (smart-emacs-lisp-mode-p) (featurep 'wtags))
472 (progn (setq tag (intern tag))
473 (cond ((fboundp tag) (describe-function tag))
474 ((boundp tag) (describe-variable tag))
475 (t (error "(smart-lisp): Unbound symbol: %s" tag))))
476 (condition-case ()
477 (funcall (if (br-in-browser)
478 'find-tag 'find-tag-other-window)
479 tag next)
480 (error (if (equal tags-file-name smart-emacs-tags-file)
481 nil
482 (setq tags-file-name smart-emacs-tags-file)
483 (funcall (if (br-in-browser)
484 'find-tag 'find-tag-other-window)
485 tag next))))))))
486
487 (defun smart-lisp-at-tag-p ()
488 "Returns Lisp tag name that point is within, else nil.
489 Returns nil when point is on the first line of a 'def' form past the first 4
490 characters."
491 (let* ((identifier-chars "-_*:+%$#!<>a-zA-Z0-9")
492 (identifier (concat "[-<*a-zA-Z][" identifier-chars "]*"))
493 (opoint (point)))
494 (save-excursion
495 (beginning-of-line)
496 (if (and (looking-at "\\(;*[ \t]*\\)?(def[^- \n\t]+[ \n\t]")
497 (> opoint (match-end 0)))
498 nil
499 (goto-char opoint)
500 (skip-chars-backward identifier-chars)
501 (if (looking-at identifier)
502 (buffer-substring (point) (match-end 0)))))))
503
504 (defun smart-lisp-mode-p ()
505 "Return t if in a mode which uses Lisp symbols."
506 (or (smart-emacs-lisp-mode-p)
507 (eq major-mode 'lisp-mode)
508 (eq major-mode 'scheme-mode)))
509
510 ;;;###autoload
511 (defun smart-objc (&optional identifier next)
512 "Jumps to the definition of optional Objective-C IDENTIFIER or the one at point.
513 Optional second arg NEXT means jump to next matching Objective-C tag.
514
515 It assumes that its caller has already checked that the key was pressed in an
516 appropriate buffer and has moved the cursor to the selected buffer.
517
518 If:
519 (1) on a '#include' statement, the include file is displayed;
520 Look for include file in directory lists 'smart-c-cpp-include-dirs'
521 and 'smart-c-include-dirs'.
522 (2) on an Objective-C identifier, the identifier definition is displayed,
523 assuming the identifier is found within an 'etags' generated tag file
524 in the current directory or any of its ancestor directories.
525 (3) if 'smart-c-use-lib-man' is non-nil, the Objective-C identifier is
526 recognized as a library symbol, and a man page is found for the
527 identifier, then the man page is displayed."
528
529 (interactive)
530 (or
531 (if identifier nil (smart-c-include-file))
532 (let ((tag (or identifier (smart-objc-at-tag-p))))
533 ;; Set free variable tags-file-name so that next 'find-tag' command uses
534 ;; whatever tags file is set here.
535 (setq tags-file-name (smart-tags-file buffer-file-name))
536 (message "Looking for '%s' in '%s'..." tag tags-file-name)
537 (condition-case ()
538 (progn
539 (funcall (if (br-in-browser)
540 'find-tag 'find-tag-other-window)
541 tag next)
542 (message "Found definition for '%s'." tag))
543 (error
544 (if (not smart-c-use-lib-man)
545 (progn (message "'%s' not found in '%s'." tag tags-file-name)
546 (beep))
547 (message
548 "Checking if '%s' is an Objective-C library function..." tag)
549 (if (smart-library-symbol tag)
550 (progn
551 (message
552 "Displaying Objective-C library man page for '%s'." tag)
553 (manual-entry tag))
554 (message "'%s' not found in '%s' or Objective-C libraries."
555 tag tags-file-name)
556 (beep))))))))
557
558 ;;; The following should be called only if the OO-Browser is available.
559 ;;;###autoload
560 (defun smart-objc-oobr (&optional junk)
561 "Jumps to the definition of selected Objective-C construct via OO-Browser support.
562 Optional JUNK is ignored. Does nothing if the OO-Browser is not available.
563
564 It assumes that its caller has already checked that the key was pressed in an
565 appropriate buffer and has moved the cursor to the selected buffer.
566
567 If key is pressed:
568 (1) on a '#include' statement, the include file is displayed;
569 Look for include file in directory lists 'smart-c-cpp-include-dirs'
570 and 'smart-c-include-dirs'.
571 (2) within a method declaration, its definition is displayed;
572 (3) on a class name, the class definition is shown.
573
574 (2) and (3) require that an OO-Browser Environment has been loaded with
575 the {M-x br-env-load RTN} command."
576
577 (interactive)
578 (objc-to-definition 'other-win))
579
580 (defun smart-objc-at-tag-p ()
581 "Return Objective-C tag name that point is within, else nil."
582 (let* ((identifier-chars "_a-zA-Z0-9")
583 (identifier
584 (concat "\\([-+][ \t]*\\)?\\([_a-zA-Z][" identifier-chars "]*\\)")))
585 (save-excursion
586 (skip-chars-backward identifier-chars)
587 (if (looking-at identifier)
588 (buffer-substring (match-beginning 2) (match-end 2))))))
589
590 ;;; ************************************************************************
591 ;;; Private functions
592 ;;; ************************************************************************
593
594 (defun smart-library-symbol (tag)
595 "Return non-nil if TAG is a library symbol listed in cache of such symbols.
596 See the \"${hyperb:dir}/smart-clib-sym\" script for more information."
597 (let ((buf (get-buffer-create "*junk*"))
598 (found))
599 (save-excursion
600 (set-buffer buf)
601 (setq buffer-read-only nil)
602 (erase-buffer)
603 (call-process (expand-file-name "smart-clib-sym" hyperb:dir)
604 nil buf nil tag)
605 (setq found (string-equal (buffer-substring 1 2) "1"))
606 (set-buffer-modified-p nil)
607 (kill-buffer buf)
608 found)))
609
610 ;;;###autoload
611 (defun smart-tags-file-path (file)
612 "Expand relative FILE name by looking it up in the nearest tags file.
613 Return FILE unchanged if it exists relative to the current directory or
614 cannot be expanded via a tags file."
615 (or (cond ((or (file-exists-p file) (file-name-absolute-p file)) file)
616 (t (let ((tags-file (smart-tags-file default-directory))
617 (file-regexp
618 (concat "\^L\n\\(.*/\\)?" (regexp-quote file) ",")))
619 (if tags-file
620 (progn
621 (set-buffer (find-file-noselect tags-file))
622 (goto-char (point-min))
623 (if (re-search-forward file-regexp nil t)
624 (expand-file-name
625 (buffer-substring (1- (match-end 0))
626 (progn (beginning-of-line)
627 (point))))))))))
628 file))
629
630 ;;;###autoload
631 (defun smart-tags-file (curr-filename)
632 "Return appropriate tags file name for CURR-FILENAME or 'tags-file-name'."
633 (let ((path curr-filename)
634 (tags-file))
635 (while (and
636 (stringp path)
637 (setq path (file-name-directory path))
638 (setq path (directory-file-name path))
639 ;; Not at root directory
640 (not (string-match ":?/\\'" path))
641 ;; No tags file
642 (not (file-exists-p
643 (setq tags-file (expand-file-name "TAGS" path)))))
644 (setq tags-file nil))
645 (if (and (not tags-file)
646 (stringp curr-filename)
647 (smart-emacs-lisp-mode-p)
648 (let ((path (file-name-directory curr-filename)))
649 (delq nil (mapcar
650 (function
651 (lambda (p)
652 (and p (equal (file-name-as-directory p)
653 path))))
654 load-path))))
655 (setq tags-file smart-emacs-tags-file))
656 (or tags-file tags-file-name
657 (call-interactively 'visit-tags-table))))
658
659 ;;; ************************************************************************
660 ;;; Private variables
661 ;;; ************************************************************************
662
663 (provide 'hmouse-tag)