diff lisp/hyperbole/hmouse-tag.el @ 100:4be1180a9e89 r20-1b2

Import from CVS: tag r20-1b2
author cvs
date Mon, 13 Aug 2007 09:15:11 +0200
parents 131b0175ea99
children
line wrap: on
line diff
--- a/lisp/hyperbole/hmouse-tag.el	Mon Aug 13 09:13:58 2007 +0200
+++ b/lisp/hyperbole/hmouse-tag.el	Mon Aug 13 09:15:11 2007 +0200
@@ -6,32 +6,17 @@
 ;; KEYWORDS:     c, hypermedia, mouse, oop, tools
 ;;
 ;; AUTHOR:       Bob Weiner
-;; ORG:          Brown U.
+;; ORG:          InfoDock Associates
 ;;
 ;; ORIG-DATE:    24-Aug-91
-;; LAST-MOD:     11-Sep-95 at 18:19:48 by Bob Weiner
-;;
-;; This file is part of Hyperbole.
-;; Available for use and distribution under the same terms as GNU Emacs.
-;;
-;; Copyright (C) 1991-1995, Free Software Foundation, Inc.
-;; Developed with support from Motorola Inc.
-;;
-;; DESCRIPTION:
-;;
-;;   Supports C, C++, Objective-C, Lisp, Fortran, and Assembly.
-;;   See the GNU Emacs manual for information on how to create a TAGS file
-;;     from the `etags' program.
-;;   Does not support the `ctags' tags file format.
-;;
-;;   YOU MUST APPROPRIATELY SET THE PUBLIC VARIABLES BELOW BEFORE USE.
-;;
-;; DESCRIP-END.
-
+;; LAST-MOD:     17-Feb-97 at 15:31:50 by Bob Weiner
 ;;; ************************************************************************
 ;;; Other required Elisp libraries
 ;;; ************************************************************************
 
+(require 'hpath)
+(require 'hbut)
+
 (if (cond ((or (featurep 'etags) (featurep 'tags))
 	   nil)
 	  ((or hyperb:lemacs-p hyperb:emacs19-p)
@@ -71,11 +56,11 @@
   "*Ordered list of directories to search for C/C++ include files.
 Each directory must end with a directory separator.  Directories normally
 searched by the C/C++ pre-processor should be set instead in
-'smart-c-cpp-include-dirs'.")
+`smart-c-cpp-include-dirs'.")
 
 (defvar smart-c-use-lib-man nil
-  "When non-nil makes 'smart-c' and 'smart-c++' display man pages for recognized lib symbols.
-When nil, 'smart-c' and 'smart-c++' look up only symbols defined in an etags
+  "When non-nil makes `smart-c' and `smart-c++' display man pages for recognized lib symbols.
+When nil, `smart-c' and `smart-c++' look up only symbols defined in an etags
 TAGS file.
 
 Create the file ~/.CLIBS-LIST and populate it with the full pathnames (one per
@@ -91,9 +76,20 @@
   "[ \t/*]*#[ \t]*\\(include\\|import\\)[ \t]+\\([\"<]\\)\\([^\">]+\\)[\">]"
   "Regexp to match to C, C++, or Objective-C include file lines.
 Include keyword matched is grouping 1.  Type of include, user-specified via
-double quote, or system-related starting with '<' is given by grouping 2.
+double quote, or system-related starting with `<' is given by grouping 2.
 File name is grouping 3.")
 
+(defvar smart-java-package-dirs
+  (and (fboundp 'getenv) (getenv "JAVA_HOME")
+       (list (expand-file-name "src/" (file-name-as-directory (getenv "JAVA_HOME")))))
+  "*Ordered list of directories to search for imported Java packages.
+Each directory must end with a directory separator.")
+
+(defconst smart-java-package-regexp
+  "[ \t/*]*\\(package\\|import\\)[ \t]+\\([^; \t\n\r\f]+\\)"
+  "Regexp to match to Java `package' and `import' lines.
+Keyword matched is grouping 1.  Referent is grouping 2.")
+
 (defvar smart-emacs-tags-file nil
   "*Full path name of etags file for GNU Emacs source.")
 
@@ -110,26 +106,24 @@
 
 If:
  (1) on an include statement, the include file is displayed;
-     Look for include file in directory list 'smart-asm-include-dirs'.
+     Look for include file in directory list `smart-asm-include-dirs'.
  (2) on an identifier, the identifier definition is displayed,
-     assuming the identifier is found within an 'etags' generated tag file
+     assuming the identifier is found within an `etags' generated tag file
      in the current directory or any of its ancestor directories."
 
   (interactive)
   (or
    (if identifier nil (smart-asm-include-file))
    (let ((tag (or identifier (smart-asm-at-tag-p))))
-     ;; Set free variable tags-file-name so that next 'find-tag' command uses
+     ;; Set free variable tags-file-name so that next `find-tag' command uses
      ;; whatever tags file is set here.
      (setq tags-file-name (smart-tags-file buffer-file-name))
-     (message "Looking for '%s' in '%s'..." tag tags-file-name)
+     (message "Looking for `%s' in `%s'..." tag tags-file-name)
      (condition-case ()
 	 (progn
-	   (funcall (if (br-in-browser)
-			'find-tag 'find-tag-other-window)
-		    tag next)
-	   (message "Found definition for '%s'." tag))
-       (error (message "'%s' not found in '%s'." tag tags-file-name)
+	   (smart-tags-display tag next)
+	   (message "Found definition for `%s'." tag))
+       (error (message "`%s' not found in `%s'." tag tags-file-name)
 	      (beep))))))
 
 ;;;###autoload
@@ -140,12 +134,422 @@
     (save-excursion
       (skip-chars-backward identifier-chars)
       (if (looking-at identifier)
-	  (buffer-substring (point) (match-end 0))))))
+	  (smart-flash-tag
+	   (buffer-substring (point) (match-end 0))
+	   (point) (match-end 0))))))
+
+(defun smart-c (&optional identifier next)
+  "Jumps to the definition of optional C IDENTIFIER or the one at point.
+Optional second arg NEXT means jump to next matching C tag.
+
+It assumes that its caller has already checked that the key was pressed in an
+appropriate buffer and has moved the cursor to the selected buffer.
+
+If:
+ (1) on a `#include' statement, the include file is displayed;
+     Look for include file in directory lists `smart-c-cpp-include-dirs'
+     and `smart-c-include-dirs'.
+ (2) on a C identifier, the identifier definition is displayed,
+     assuming the identifier is found within an `etags' generated tag file
+     in the current directory or any of its ancestor directories.
+ (3) if `smart-c-use-lib-man' is non-nil, the C identifier is
+     recognized as a library symbol, and a man page is found for the
+     identifier, then the man page is displayed."
+
+  (interactive)
+  (or
+   (if identifier nil (smart-c-include-file))
+   (let ((tag (or identifier (smart-c-at-tag-p))))
+     ;; Set free variable tags-file-name so that next `find-tag' command uses
+     ;; whatever tags file is set here.
+     (setq tags-file-name (smart-tags-file buffer-file-name))
+     (message "Looking for `%s' in `%s'..." tag tags-file-name)
+     (condition-case ()
+	 (progn
+	   (smart-tags-display tag next)
+	   (message "Found definition for `%s'." tag))
+       (error
+	(if (not smart-c-use-lib-man)
+	    (progn (message "`%s' not found in `%s'." tag tags-file-name)
+		   (beep))
+	  (message "Checking if `%s' is a C library function..." tag)
+	  (if (smart-library-symbol tag)
+	      (progn (message "Displaying C library man page for `%s'." tag)
+		     (manual-entry tag))
+	    (message "`%s' not found in `%s' or C libraries."
+		     tag tags-file-name)
+	    (beep))))))))
+
+;;;###autoload
+(defun smart-c-at-tag-p ()
+  "Return C tag name that point is within, else nil."
+  (let* ((identifier-chars "_a-zA-Z0-9")
+	 (identifier (concat "[_a-zA-Z][" identifier-chars "]*")))
+    (save-excursion
+      (skip-chars-backward identifier-chars)
+      (if (looking-at identifier)
+	  (smart-flash-tag
+	   (buffer-substring (point) (match-end 0))
+	   (point) (match-end 0))))))
+
+;;;###autoload
+(defun smart-c++ (&optional identifier next)
+  "Jumps to the definition of optional C++ IDENTIFIER or the one at point.
+Optional second arg NEXT means jump to next matching C++ tag.
+
+It assumes that its caller has already checked that the key was pressed in an
+appropriate buffer and has moved the cursor to the selected buffer.
+
+If:
+ (1) on a `#include' statement, the include file is displayed;
+     Look for include file in directory lists `smart-c-cpp-include-dirs'
+     and `smart-c-include-dirs'.
+ (2) on a C++ identifier, the identifier definition is displayed,
+     assuming the identifier is found within an `etags' generated tag file
+     in the current directory or any of its ancestor directories.
+ (3) if `smart-c-use-lib-man' is non-nil, the C++ identifier is
+     recognized as a library symbol, and a man page is found for the
+     identifier, then the man page is displayed."
+
+  (interactive)
+  (if (fboundp 'c++-to-definition)
+      ;; Only fboundp if OO-Browser has been loaded.
+      (smart-c++-oo-browser)
+    (or
+     (if identifier nil (smart-c-include-file))
+     (let ((tag (or identifier (smart-c++-at-tag-p))))
+       ;; Set free variable tags-file-name so that next `find-tag' command uses
+       ;; whatever tags file is set here.
+       (setq tags-file-name (smart-tags-file buffer-file-name))
+       (message "Looking for `%s' in `%s'..." tag tags-file-name)
+       (condition-case ()
+	   (progn
+	     (smart-tags-display tag next)
+	     (message "Found definition for `%s'." tag))
+	 (error
+	  (if (not smart-c-use-lib-man)
+	      (progn (message "`%s' not found in `%s'." tag tags-file-name)
+		     (beep))
+	    (message "Checking if `%s' is a C++ library function..." tag)
+	    (if (smart-library-symbol tag)
+		(progn (message "Displaying C++ library man page for `%s'." tag)
+		       (manual-entry tag))
+	      (message "`%s' not found in `%s' or C++ libraries."
+		       tag tags-file-name)
+	      (beep)))))))))
+
+;;; The following should be called only if the OO-Browser is available.
+(defun smart-c++-oo-browser (&optional junk)
+  "Jumps to the definition of selected C++ construct via OO-Browser support.
+Optional JUNK is ignored.  Does nothing if the OO-Browser is not available.
+
+It assumes that its caller has already checked that the key was pressed in an
+appropriate buffer and has moved the cursor to the selected buffer.
+
+If key is pressed:
+ (1) on a `#include' statement, the include file is displayed;
+     Look for include file in directory lists `smart-c-cpp-include-dirs'
+     and `smart-c-include-dirs'.
+ (2) within a method declaration, its definition is displayed;
+ (3) on a class name, the class definition is shown.
+
+ (2) and (3) require that an OO-Browser Environment has been loaded with
+     the {M-x br-env-load RET} command."
+
+  (interactive)
+  (c++-to-definition t))
+
+(defun smart-c++-at-tag-p ()
+  "Return C++ tag name that point is within, else nil."
+  (let* ((identifier-chars "_:~<>a-zA-Z0-9")
+	 (identifier (concat "\\([_~:<a-zA-Z][" identifier-chars "]*"
+			     "[ \t]*[^]) \t:;.,?~{}][^[( \t:;.,~^!|?{}]?[=*]?\\)[ \t\n]*(")))
+    (save-excursion
+      (skip-chars-backward identifier-chars)
+      (if (looking-at identifier)
+	  (smart-flash-tag
+	   (buffer-substring (match-beginning 1) (match-end 1))
+	   (match-beginning 1) (match-end 1))))))
+
+(defun smart-emacs-lisp-mode-p ()
+  "Return t if in a mode which uses Emacs Lisp symbols."
+  (or (memq major-mode '(emacs-lisp-mode lisp-interaction-mode debugger-mode))
+      ;; Emacs Lisp symbols appear in Help buffers frequently.
+      (string-match "Help\\*$" (buffer-name))))
+
+(defun smart-fortran (&optional identifier next)
+  "Jumps to the definition of optional Fortran IDENTIFIER or the one at point.
+Optional second arg NEXT means jump to next matching Fortran tag.
+
+It assumes that its caller has already checked that the key was pressed in an
+appropriate buffer and has moved the cursor to the selected buffer.
+
+If on a Fortran identifier, the identifier definition is displayed,
+assuming the identifier is found within an `etags' generated tag file
+in the current directory or any of its ancestor directories."
+  (interactive)
+  (let ((tag (or identifier (smart-fortran-at-tag-p))))
+    ;; Set free variable tags-file-name so that next `find-tag' command uses
+    ;; whatever tags file is set here.
+    (setq tags-file-name (smart-tags-file buffer-file-name))
+    (message "Looking for `%s' in `%s'..." tag tags-file-name)
+    (condition-case ()
+	(progn
+	  (smart-tags-display tag next)
+	  (message "Found definition for `%s'." tag))
+      (error
+       (message "`%s' not found in `%s'." tag tags-file-name)
+       (beep)))))
+
+;;;###autoload
+(defun smart-fortran-at-tag-p ()
+  "Return Fortran tag name that point is within, else nil."
+  (let* ((identifier-chars "_a-zA-Z0-9")
+	 (identifier (concat "[_a-zA-Z][" identifier-chars "]*")))
+    (save-excursion
+      (skip-chars-backward identifier-chars)
+      (if (looking-at identifier)
+	  (smart-flash-tag
+	   (buffer-substring (point) (match-end 0))
+	   (point) (match-end 0))))))
+
+;;;###autoload
+(defun smart-java (&optional identifier next)
+  "Jumps to the definition of optional Java IDENTIFIER or the one at point.
+Optional second arg NEXT means jump to next matching Java tag.
+
+It assumes that its caller has already checked that the key was pressed in an
+appropriate buffer and has moved the cursor to the selected buffer.
+
+If:
+ (1) within a commented @see cross-reference, the referent is displayed;
+ (2) on a `package' or `import' statement, the referent is displayed;
+     Look for referent files in the directory list `smart-java-package-dirs'.
+ (3) on an Java identifier, the identifier definition is displayed,
+     assuming the identifier is found within an `etags' generated tag file
+     in the current directory or any of its ancestor directories."
+
+  (interactive)
+  (if (fboundp 'java-to-definition)
+      ;; Only fboundp if OO-Browser has been loaded.
+      (smart-java-oo-browser)
+    (or
+     (if identifier nil (or (smart-java-cross-reference) (smart-java-packages)))
+     (let ((tag (or identifier (smart-java-at-tag-p))))
+       ;; Set free variable tags-file-name so that next `find-tag' command uses
+       ;; whatever tags file is set here.
+       (setq tags-file-name (smart-tags-file buffer-file-name))
+       (message "Looking for `%s' in `%s'..." tag tags-file-name)
+       (condition-case ()
+	   (progn
+	     (smart-tags-display tag next)
+	     (message "Found definition for `%s'." tag))
+	 (error (progn (message "`%s' not found in `%s'." tag tags-file-name)
+		       (beep))))))))
+
+;;; The following should be called only if the OO-Browser is available.
+(defun smart-java-oo-browser (&optional junk)
+  "Jumps to the definition of selected Java construct via OO-Browser support.
+Optional JUNK is ignored.  Does nothing if the OO-Browser is not available.
+
+It assumes that its caller has already checked that the key was pressed in an
+appropriate buffer and has moved the cursor to the selected buffer.
+
+If key is pressed:
+ (1) within a commented @see cross-reference, the referent is displayed;
+ (2) on a `package' or `import' statement, the referent is displayed;
+     Look for referent files in the directory list `smart-java-package-dirs'.
+ (3) within a method declaration, its definition is displayed;
+ (4) on a class name, the class definition is shown."
+
+  (interactive)
+  (or (smart-java-cross-reference)
+      (smart-java-packages)
+      (java-to-definition t)))
+
+;;;###autoload
+(defun smart-java-at-tag-p ()
+  "Return Java tag name that point is within, else nil."
+  (let* ((identifier-chars "_$.a-zA-Z0-9")
+	 (identifier
+	  (concat "[_$a-zA-Z][" identifier-chars "]*")))
+    (save-excursion
+      (skip-chars-backward identifier-chars)
+      (if (looking-at identifier)
+	  (smart-flash-tag
+	   (buffer-substring (point) (match-end 0))
+	   (point) (match-end 0))))))
+
+(defun smart-lisp (&optional next)
+  "Jumps to the definition of any selected Lisp construct.
+If on an Emacs Lisp require, load, or autoload clause and `find-library'
+from load-library package by Hallvard Furuseth (hallvard@ifi.uio.no) has
+been loaded, jumps to library source, if possible.
+
+Otherwise, the construct must be found within an `etags' generated tag file
+in the current directory or any of its ancestor directories in order for its
+definition to be located.
+
+Optional NEXT means jump to next matching Lisp tag.  When matching to an Emacs
+Lisp tag using `wtags' (Bob Weiner's personal modifications to `etags'),
+there is no next tag, so display documentation for current tag instead.
+
+This command assumes that its caller has already checked that the key was
+pressed in an appropriate buffer and has moved the cursor to the selected
+buffer."
+
+  (interactive)
+  ;; Handle `require', `load', and `autoload' clauses in Emacs Lisp.
+  (or (and (fboundp 'find-library)
+	   (smart-emacs-lisp-mode-p)
+	   (let ((req)
+		 (opoint (point)))
+	     (setq req (and (search-backward "\(" nil t)
+			    (looking-at (concat
+					 "(\\(require\\|load\\|autoload\\)"
+					 "[ \t]+.*['\"]"
+					 "\\([^][() \t\n\^M`'\"]+\\)"))))
+	     (goto-char opoint)
+	     (if req (progn
+		       (setq req (buffer-substring (match-beginning 2)
+						   (match-end 2)))
+		       (hpath:display-buffer (current-buffer))
+		       (find-library req)
+		       t))))
+      (let ((tag (smart-lisp-at-tag-p)))
+	;; Set free variable tags-file-name so that next `find-tag' command
+	;; uses whatever tags file is set here.
+	(setq tags-file-name (smart-tags-file default-directory))
+	;; This part only works properly for Emacs Lisp, so is conditionalized.
+	(if (and next (smart-emacs-lisp-mode-p) (featurep 'wtags))
+	    (progn (setq tag (intern tag))
+		   (cond ((fboundp tag) (describe-function tag))
+			 ((boundp tag) (describe-variable tag))
+			 (t (error "(smart-lisp): Unbound symbol: %s" tag))))
+	  (condition-case ()
+	      (smart-tags-display tag next)
+	    (error (if (equal tags-file-name smart-emacs-tags-file)
+		       (progn (message "`%s' not found in `%s'."
+				       tag tags-file-name)
+			      (beep))
+		     (setq tags-file-name smart-emacs-tags-file)
+		     (smart-tags-display tag next))))))))
+
+(defun smart-lisp-at-tag-p ()
+  "Returns Lisp tag name that point is within, else nil.
+Returns nil when point is within a Lisp `def' keyword."
+  (let* ((identifier-chars "-_*:+%$#!<>a-zA-Z0-9")
+	 (identifier (concat "[-<*a-zA-Z][" identifier-chars "]*"))
+	 (opoint (point)))
+    (save-excursion
+      (beginning-of-line)
+      (if (and (looking-at "\\(;*[ \t]*\\)?(def[^- \n\t]+")
+	       (< opoint (match-end 0)))
+	  nil
+	(goto-char opoint)
+	(skip-chars-backward identifier-chars)
+	(if (looking-at identifier)
+	  (smart-flash-tag
+	   (buffer-substring (point) (match-end 0))
+	   (point) (match-end 0)))))))
+
+;;;###autoload
+(defun smart-lisp-mode-p ()
+  "Return t if in a mode which uses Lisp symbols."
+  (or (smart-emacs-lisp-mode-p)
+      (memq major-mode '(lisp-mode scheme-mode))))
+
+;;;###autoload
+(defun smart-objc (&optional identifier next)
+  "Jumps to the definition of optional Objective-C IDENTIFIER or the one at point.
+Optional second arg NEXT means jump to next matching Objective-C tag.
+
+It assumes that its caller has already checked that the key was pressed in an
+appropriate buffer and has moved the cursor to the selected buffer.
+
+If:
+ (1) on a `#include' statement, the include file is displayed;
+     Look for include file in directory lists `smart-c-cpp-include-dirs'
+     and `smart-c-include-dirs'.
+ (2) on an Objective-C identifier, the identifier definition is displayed,
+     assuming the identifier is found within an `etags' generated tag file
+     in the current directory or any of its ancestor directories.
+ (3) if `smart-c-use-lib-man' is non-nil, the Objective-C identifier is
+     recognized as a library symbol, and a man page is found for the
+     identifier, then the man page is displayed."
+
+  (interactive)
+  
+  (if (fboundp 'objc-to-definition)
+      ;; Only fboundp if OO-Browser has been loaded.
+      (smart-objc-oo-browser)
+    (or
+     (if identifier nil (smart-c-include-file))
+     (let ((tag (or identifier (smart-objc-at-tag-p))))
+       ;; Set free variable tags-file-name so that next `find-tag' command uses
+       ;; whatever tags file is set here.
+       (setq tags-file-name (smart-tags-file buffer-file-name))
+       (message "Looking for `%s' in `%s'..." tag tags-file-name)
+       (condition-case ()
+	   (progn
+	     (smart-tags-display tag next)
+	     (message "Found definition for `%s'." tag))
+	 (error
+	  (if (not smart-c-use-lib-man)
+	      (progn (message "`%s' not found in `%s'." tag tags-file-name)
+		     (beep))
+	    (message
+	     "Checking if `%s' is an Objective-C library function..." tag)
+	    (if (smart-library-symbol tag)
+		(progn
+		  (message
+		   "Displaying Objective-C library man page for `%s'." tag)
+		  (manual-entry tag))
+	      (message "`%s' not found in `%s' or Objective-C libraries."
+		       tag tags-file-name)
+	      (beep)))))))))
+
+;;; The following should be called only if the OO-Browser is available.
+(defun smart-objc-oo-browser (&optional junk)
+  "Jumps to the definition of selected Objective-C construct via OO-Browser support.
+Optional JUNK is ignored.  Does nothing if the OO-Browser is not available.
+
+It assumes that its caller has already checked that the key was pressed in an
+appropriate buffer and has moved the cursor to the selected buffer.
+
+If key is pressed:
+ (1) on a `#include' statement, the include file is displayed;
+     Look for include file in directory lists `smart-c-cpp-include-dirs'
+     and `smart-c-include-dirs'.
+ (2) within a method declaration, its definition is displayed;
+ (3) on a class name, the class definition is shown.
+
+ (2) and (3) require that an OO-Browser Environment has been loaded with
+     the {M-x br-env-load RET} command."
+
+  (interactive)
+  (objc-to-definition t))
+
+(defun smart-objc-at-tag-p ()
+  "Return Objective-C tag name that point is within, else nil."
+  (let* ((identifier-chars "_a-zA-Z0-9")
+	 (identifier
+	  (concat "\\([-+][ \t]*\\)?\\([_a-zA-Z][" identifier-chars "]*\\)")))
+    (save-excursion
+      (skip-chars-backward identifier-chars)
+      (if (looking-at identifier)
+	  (smart-flash-tag
+	   (buffer-substring (match-beginning 2) (match-end 2))
+	   (match-beginning 2) (match-end 2))))))
+
+;;; ************************************************************************
+;;; Private functions
+;;; ************************************************************************
 
 (defun smart-asm-include-file ()
   "If point is on an include file line, tries to display file.
 Returns non-nil iff on an include file line, even if file is not found.
-Look for include file in 'smart-asm-include-dirs' and add suffix \".ins\" or
+Look for include file in `smart-asm-include-dirs' and add suffix \".ins\" or
 \".inc\" to filename if it lacks a suffix." 
   (let ((opoint (point)))
     ;; Some assemblers utilize the C preprocessor, so try that first.
@@ -164,7 +568,7 @@
 	     (while dir-list
 	       (setq dir-list
 		     (if (setq path (car (directory-files
-					   (car dir-list) t file)))
+					  (car dir-list) t file)))
 			 nil
 		       (cdr dir-list))))
 	     ;;
@@ -173,88 +577,32 @@
 	     (if path
 		 (if (and (file-readable-p path)
 			  (progn
-			    (if (br-in-browser)
-				(find-file path)
-			      (find-file-other-window path))
+			    (hpath:find path)
 			    (cond ((featurep 'asm-mode) t)
 				  ((load "asm-mode" nil 'nomessage)
 				   (provide 'asm-mode))
 				  (t
-				    (beep)
-				    (message
-				      "(smart-asm-include-file):  asm-mode undefined.")
-				    nil
-				    ))))
+				   (beep)
+				   (message
+				    "(smart-asm-include-file):  asm-mode undefined.")
+				   nil
+				   ))))
 		     nil
 		   (beep)
-		   (message "(smart-asm-include-file):  '%s' unreadable." path))
+		   (message "(smart-asm-include-file):  `%s' unreadable." path))
 	       (beep)
-	       (message "(smart-asm-include-file):  '%s' not found." file))
+	       (message "(smart-asm-include-file):  `%s' not found." file))
 	     path))
 	  ;; not on an include file line
 	  (t (goto-char opoint)
 	     nil))))
 
 
-(defun smart-c (&optional identifier next)
-  "Jumps to the definition of optional C IDENTIFIER or the one at point.
-Optional second arg NEXT means jump to next matching C tag.
-
-It assumes that its caller has already checked that the key was pressed in an
-appropriate buffer and has moved the cursor to the selected buffer.
-
-If:
- (1) on a '#include' statement, the include file is displayed;
-     Look for include file in directory lists 'smart-c-cpp-include-dirs'
-     and 'smart-c-include-dirs'.
- (2) on a C identifier, the identifier definition is displayed,
-     assuming the identifier is found within an 'etags' generated tag file
-     in the current directory or any of its ancestor directories.
- (3) if 'smart-c-use-lib-man' is non-nil, the C identifier is
-     recognized as a library symbol, and a man page is found for the
-     identifier, then the man page is displayed."
-
-  (interactive)
-  (or
-   (if identifier nil (smart-c-include-file))
-   (let ((tag (or identifier (smart-c-at-tag-p))))
-     ;; Set free variable tags-file-name so that next 'find-tag' command uses
-     ;; whatever tags file is set here.
-     (setq tags-file-name (smart-tags-file buffer-file-name))
-     (message "Looking for '%s' in '%s'..." tag tags-file-name)
-     (condition-case ()
-	 (progn
-	   (funcall (if (br-in-browser)
-			'find-tag 'find-tag-other-window)
-		    tag next)
-	   (message "Found definition for '%s'." tag))
-       (error
-	(if (not smart-c-use-lib-man)
-	    (progn (message "'%s' not found in '%s'." tag tags-file-name)
-		   (beep))
-	  (message "Checking if '%s' is a C library function..." tag)
-	  (if (smart-library-symbol tag)
-	      (progn (message "Displaying C library man page for '%s'." tag)
-		     (manual-entry tag))
-	    (message "'%s' not found in '%s' or C libraries."
-		     tag tags-file-name)
-	    (beep))))))))
-
-;;;###autoload
-(defun smart-c-at-tag-p ()
-  "Return C tag name that point is within, else nil."
-  (let* ((identifier-chars "_a-zA-Z0-9")
-	 (identifier (concat "[_a-zA-Z][" identifier-chars "]*")))
-    (save-excursion
-      (skip-chars-backward identifier-chars)
-      (if (looking-at identifier)
-	  (buffer-substring (point) (match-end 0))))))
-
 (defun smart-c-include-file ()
   "If point is on an include file line, tries to display file.
 Returns non-nil iff on an include file line, even if file is not found.
-Look for include file in 'smart-c-cpp-include-dirs' and in directory list
-'smart-c-include-dirs'."
+Look for include file in `smart-c-cpp-include-dirs' and in directory list
+`smart-c-include-dirs'."
   (let ((opoint (point)))
     (beginning-of-line)
     (if (looking-at smart-c-include-regexp)
@@ -281,9 +629,7 @@
 	  (if found
 	      (if (and (file-readable-p path)
 		       (progn
-			 (if (br-in-browser)
-			     (find-file path)
-			   (find-file-other-window path))
+			 (hpath:find path)
 			 (cond ((or (featurep 'cc-mode)
 				    (featurep 'c-mode))
 				t)
@@ -298,298 +644,198 @@
 				))))
 		  nil
 		(beep)
-		(message "(smart-c-include-file):  '%s' unreadable." path))
+		(message "(smart-c-include-file):  `%s' unreadable." path))
 	    (beep)
-	    (message "(smart-c-include-file):  '%s' not found." file))
+	    (message "(smart-c-include-file):  `%s' not found." file))
 	  path)
       (goto-char opoint)
       nil)))
 
-
-;;;###autoload
-(defun smart-c++ (&optional identifier next)
-  "Jumps to the definition of optional C++ IDENTIFIER or the one at point.
-Optional second arg NEXT means jump to next matching C++ tag.
-
-It assumes that its caller has already checked that the key was pressed in an
-appropriate buffer and has moved the cursor to the selected buffer.
-
-If:
- (1) on a '#include' statement, the include file is displayed;
-     Look for include file in directory lists 'smart-c-cpp-include-dirs'
-     and 'smart-c-include-dirs'.
- (2) on a C++ identifier, the identifier definition is displayed,
-     assuming the identifier is found within an 'etags' generated tag file
-     in the current directory or any of its ancestor directories.
- (3) if 'smart-c-use-lib-man' is non-nil, the C++ identifier is
-     recognized as a library symbol, and a man page is found for the
-     identifier, then the man page is displayed."
-
-  (interactive)
-  (or
-   (if identifier nil (smart-c-include-file))
-   (let ((tag (or identifier (smart-c++-at-tag-p))))
-     ;; Set free variable tags-file-name so that next 'find-tag' command uses
-     ;; whatever tags file is set here.
-     (setq tags-file-name (smart-tags-file buffer-file-name))
-     (message "Looking for '%s' in '%s'..." tag tags-file-name)
-     (condition-case ()
-	 (progn
-	   (funcall (if (br-in-browser)
-			'find-tag 'find-tag-other-window)
-		    tag next)
-	   (message "Found definition for '%s'." tag))
-       (error
-	(if (not smart-c-use-lib-man)
-	    (progn (message "'%s' not found in '%s'." tag tags-file-name)
-		   (beep))
-	  (message "Checking if '%s' is a C++ library function..." tag)
-	  (if (smart-library-symbol tag)
-	      (progn (message "Displaying C++ library man page for '%s'." tag)
-		     (manual-entry tag))
-	    (message "'%s' not found in '%s' or C++ libraries."
-		     tag tags-file-name)
-	    (beep))))))))
-
-;;; The following should be called only if the OO-Browser is available.
-;;;###autoload
-(defun smart-c++-oobr (&optional junk)
-  "Jumps to the definition of selected C++ construct via OO-Browser support.
-Optional JUNK is ignored.  Does nothing if the OO-Browser is not available.
-
-It assumes that its caller has already checked that the key was pressed in an
-appropriate buffer and has moved the cursor to the selected buffer.
-
-If key is pressed:
- (1) on a '#include' statement, the include file is displayed;
-     Look for include file in directory lists 'smart-c-cpp-include-dirs'
-     and 'smart-c-include-dirs'.
- (2) within a method declaration, its definition is displayed;
- (3) on a class name, the class definition is shown.
-
- (2) and (3) require that an OO-Browser Environment has been loaded with
-     the {M-x br-env-load RTN} command."
-
-  (interactive)
-  (c++-to-definition 'other-win))
+(defun smart-flash-tag (tag start end)
+  "Tries to flash TAG at START to END in buffer, to indicate that it is serving as a hyperlink button.
+Returns TAG."
+  ;; Button flashing code might not yet have been loaded if the whole
+  ;; Hyperbole system has not been started.
+  (if (fboundp 'hui:but-flash)
+      (progn
+	(ibut:label-set tag start end)
+	(hui:but-flash)))
+  tag)
 
-(defun smart-c++-at-tag-p ()
-  "Return C++ tag name that point is within, else nil."
-  (let* ((identifier-chars "_:~<>a-zA-Z0-9")
-	 (identifier (concat "\\([_~:<a-zA-Z][" identifier-chars "]*"
-			     "[ \t]*[^]) \t:;.,?~{}][^[( \t:;.,~^!|?{}]?[=*]?\\)[ \t\n]*(")))
-    (save-excursion
-      (skip-chars-backward identifier-chars)
-      (if (looking-at identifier)
-	  (buffer-substring (point) (match-end 1))))))
-
-(defun smart-emacs-lisp-mode-p ()
-  "Return t if in a mode which uses Emacs Lisp symbols."
-  (or (eq major-mode 'emacs-lisp-mode)
-      (eq major-mode 'lisp-interaction-mode)
-      (eq major-mode 'debugger-mode)
-      ;; Emacs Lisp symbols appear in Help buffers frequently.
-      (string-match "Help\\*$" (buffer-name))))
-
-(defun smart-fortran (&optional identifier next)
-  "Jumps to the definition of optional Fortran IDENTIFIER or the one at point.
-Optional second arg NEXT means jump to next matching Fortran tag.
-
-It assumes that its caller has already checked that the key was pressed in an
-appropriate buffer and has moved the cursor to the selected buffer.
-
-If on a Fortran identifier, the identifier definition is displayed,
-assuming the identifier is found within an 'etags' generated tag file
-in the current directory or any of its ancestor directories."
-  (interactive)
-  (let ((tag (or identifier (smart-fortran-at-tag-p))))
-    ;; Set free variable tags-file-name so that next 'find-tag' command uses
-    ;; whatever tags file is set here.
-    (setq tags-file-name (smart-tags-file buffer-file-name))
-    (message "Looking for '%s' in '%s'..." tag tags-file-name)
-    (condition-case ()
-	(progn
-	  (funcall (if (br-in-browser)
-		       'find-tag 'find-tag-other-window)
-		   tag next)
-	  (message "Found definition for '%s'." tag))
-      (error
-       (message "'%s' not found in '%s'." tag tags-file-name)
-       (beep)))))
-
-;;;###autoload
-(defun smart-fortran-at-tag-p ()
-  "Return Fortran tag name that point is within, else nil."
-  (let* ((identifier-chars "_a-zA-Z0-9")
-	 (identifier (concat "[_a-zA-Z][" identifier-chars "]*")))
-    (save-excursion
-      (skip-chars-backward identifier-chars)
-      (if (looking-at identifier)
-	  (buffer-substring (point) (match-end 0))))))
-
-(defun smart-lisp (&optional next)
-  "Jumps to the definition of any selected Lisp construct.
-If on an Emacs Lisp require, load, or autoload clause and 'find-library'
-from load-library package by Hallvard Furuseth (hallvard@ifi.uio.no) has
-been loaded, jumps to library source, if possible.
-
-Otherwise, the construct must be found within an 'etags' generated tag file
-in the current directory or any of its ancestor directories in order for its
-definition to be located.
-
-Optional NEXT means jump to next matching Lisp tag.  When matching to an Emacs
-Lisp tag using 'wtags' (Bob Weiner's personal modifications to 'etags'),
-there is no next tag, so display documentation for current tag instead.
-
-This command assumes that its caller has already checked that the key was
-pressed in an appropriate buffer and has moved the cursor to the selected
-buffer."
+(defun smart-java-cross-reference ()
+  "If within a Java @see comment, displays the associated definition for editing and returns non-nil, else nil.
+Non-nil is returned even if the @see referent cannot be found.
 
-  (interactive)
-  ;; Handle 'require', 'load', and 'autoload' clauses in Emacs Lisp.
-  (or (and (fboundp 'find-library)
-	   (smart-emacs-lisp-mode-p)
-	   (let ((req)
-		 (opoint (point)))
-	     (setq req (and (search-backward "\(" nil t)
-			    (looking-at (concat
-					 "(\\(require\\|load\\|autoload\\)"
-					 "[ \t]+.*['\"]"
-					 "\\([^][() \t\n\^M`'\"]+\\)"))))
-	     (goto-char opoint)
-	     (if req (progn
-		       (setq req (buffer-substring (match-beginning 2)
-						   (match-end 2)))
-		       (pop-to-buffer nil t)
-		       (find-library req)
-		       t))))
-      (let ((tag (smart-lisp-at-tag-p)))
-	;; Set free variable tags-file-name so that next 'find-tag' command
-	;; uses whatever tags file is set here.
-	(setq tags-file-name (smart-tags-file default-directory))
-	;; This part only works properly for Emacs Lisp, so is conditionalized.
-	(if (and next (smart-emacs-lisp-mode-p) (featurep 'wtags))
-	    (progn (setq tag (intern tag))
-		   (cond ((fboundp tag) (describe-function tag))
-			 ((boundp tag) (describe-variable tag))
-			 (t (error "(smart-lisp): Unbound symbol: %s" tag))))
-	  (condition-case ()
-	      (funcall (if (br-in-browser)
-			   'find-tag 'find-tag-other-window)
-		       tag next)
-	    (error (if (equal tags-file-name smart-emacs-tags-file)
-		       nil
-		     (setq tags-file-name smart-emacs-tags-file)
-		     (funcall (if (br-in-browser)
-				  'find-tag 'find-tag-other-window)
-			      tag next))))))))
-
-(defun smart-lisp-at-tag-p ()
-  "Returns Lisp tag name that point is within, else nil.
-Returns nil when point is on the first line of a 'def' form past the first 4
-characters."
-  (let* ((identifier-chars "-_*:+%$#!<>a-zA-Z0-9")
-	 (identifier (concat "[-<*a-zA-Z][" identifier-chars "]*"))
-	 (opoint (point)))
-    (save-excursion
-      (beginning-of-line)
-      (if (and (looking-at "\\(;*[ \t]*\\)?(def[^- \n\t]+[ \n\t]")
-	       (> opoint (match-end 0)))
-	  nil
-	(goto-char opoint)
-	(skip-chars-backward identifier-chars)
-	(if (looking-at identifier)
-	    (buffer-substring (point) (match-end 0)))))))
-
-(defun smart-lisp-mode-p ()
-  "Return t if in a mode which uses Lisp symbols."
-  (or (smart-emacs-lisp-mode-p)
-      (eq major-mode 'lisp-mode)
-      (eq major-mode 'scheme-mode)))
-
-;;;###autoload
-(defun smart-objc (&optional identifier next)
-  "Jumps to the definition of optional Objective-C IDENTIFIER or the one at point.
-Optional second arg NEXT means jump to next matching Objective-C tag.
-
-It assumes that its caller has already checked that the key was pressed in an
-appropriate buffer and has moved the cursor to the selected buffer.
+Does nothing if the `oo-browser' command is undefined, since it requires that
+package for class and feature lookups."
+  ;;
+  ;; Valid forms of @see cross-references are:
+  ;;    * @see #getComponent                        - current class attribute
+  ;;    * @see #waitForAll()                        - current class method, no arguments
+  ;;    * @see #checkID(int, boolean)               - current class method, with arguments
+  ;;    * @see java.awt.ColorModel#getRGBdefault    - library class method
+  ;;    * @see Component#paintAll                   - class method
+  ;;    * @see java.awt.GridBagLayout               - library class
+  ;;    * @see Container                            - class
+  ;;
+  ;; For simplicity sake, this code ignores the library path given with any
+  ;; class in favor of the OO-Browser's lookup tables.  It also ignores any
+  ;; parameters associated with a method, and thus cannot distinguish between
+  ;; methods with the same name within a single class, which we believe to be
+  ;; fairly bad form anyway.
+  ;;
+  (let ((opoint (point)))
+    (if (and (eq major-mode 'java-mode) buffer-file-name
+	     (fboundp 'br-env-load)
+	     (or (looking-at "@see[ \t]+")
+		 (and (re-search-backward "[@\n\r\f]" nil t)
+		      (looking-at "@see[ \t]+"))))
+	(let ((class) (feature))
+	  ;; Ignore any library path preceding a classname (grouping 1)
+	  (cond
+	   ((looking-at
+	     "@see[ \t]+\\(#\\)?\\([^][(){} \t\n\r\f#]+[.]\\)?\\([^][(){} \t\n\r\f#.]+\\)[][(){} \t\n\r\f]")
+	    (if (match-beginning 1)
+		(setq class nil
+		      feature (buffer-substring (match-beginning 3)
+						(match-end 3)))
+	      (setq class (buffer-substring (match-beginning 3) (match-end 3))
+		    feature nil)))
+	   ((looking-at
+	     "@see[ \t]+\\([^][(){} \t\n\r\f#]+[.]\\)?\\([^][(){} \t\n\r\f#.]+\\)#\\([^][(){} \t\n\r\f#.]+\\)")
+	    (setq class (buffer-substring (match-beginning 2)
+					  (match-end 2))
+		  feature (buffer-substring (match-beginning 3)
+					    (match-end 3)))))
+	  ;; Return to original point.
+	  (goto-char opoint)
+	  ;; Lookup class / feature.
+	  (cond
+	   ((and (null class) (null feature))
+	    ;; Invalid or unrecognized @see format, so ignore.
+	    (message "(smart-java-cross-reference): Invalid @see cross-reference format.")
+	    (beep)
+	    t)
+	   ;; Ensure that a Java OO-Browser environment has been loaded.
+	   (t (if (or (and (boundp 'br-lang-prefix)
+			   (equal br-lang-prefix "java-")
+			   (boundp 'br-env-file) (stringp br-env-file)
+			   (null br-env-spec))
+		      ;; Load an existing Environment based on current
+		      ;; buffer or prompt to build one.  This also
+		      ;; loads the "br-java.el" library in which the
+		      ;; `java-class-def-regexp' variable used below
+		      ;; is defined.
+		      (and (br-env-load
+			    (smart-tags-file
+			     buffer-file-name
+			     (if (boundp 'br-env-default-file)
+				 br-env-default-file "OOBR")))
+			   (equal br-lang-prefix "java-")))
+		  (cond ((null feature)
+			 (br-edit nil class))
+			(t
+			 (if (null class)
+			     (if (save-excursion
+				   (or (re-search-backward java-class-def-regexp nil t)
+				       (re-search-forward java-class-def-regexp nil t)))
+				 (setq class (buffer-substring
+					      (match-beginning java-class-def-name-grpn)
+					      (match-end java-class-def-name-grpn)))
+			       (error "(smart-java-cross-reference): This @see must be in a class definition.")))
+			 (br-edit-feature class feature t)))
+		(error "(smart-java-cross-reference): The OO-Browser failed to load a Java environment.")))))
+      ;; Return to original point.
+      (goto-char opoint)
+      nil)))
 
-If:
- (1) on a '#include' statement, the include file is displayed;
-     Look for include file in directory lists 'smart-c-cpp-include-dirs'
-     and 'smart-c-include-dirs'.
- (2) on an Objective-C identifier, the identifier definition is displayed,
-     assuming the identifier is found within an 'etags' generated tag file
-     in the current directory or any of its ancestor directories.
- (3) if 'smart-c-use-lib-man' is non-nil, the Objective-C identifier is
-     recognized as a library symbol, and a man page is found for the
-     identifier, then the man page is displayed."
+(defun smart-java-library-path (library-name)
+  "Search up directory tree from current directory for a match to LIBRARY-NAME."
+  (let ((path default-directory)
+	(library-path)
+	(library-regexp (if (string-match "\\.\\|'//" library-name)
+			    (regexp-quote
+			     (concat (file-name-as-directory "")
+				     (substring library-name 0 (match-beginning 0))
+				     (file-name-as-directory "")))))
+	(start 0))
+    ;; Return rightmost match to first part of library-name.
+    (if library-regexp
+	(while (string-match library-regexp path start)
+	  (setq start (1+ (match-beginning 0))
+		library-path (substring path 0 (match-beginning 0)))))
+    library-path))
 
-  (interactive)
-  (or
-   (if identifier nil (smart-c-include-file))
-   (let ((tag (or identifier (smart-objc-at-tag-p))))
-     ;; Set free variable tags-file-name so that next 'find-tag' command uses
-     ;; whatever tags file is set here.
-     (setq tags-file-name (smart-tags-file buffer-file-name))
-     (message "Looking for '%s' in '%s'..." tag tags-file-name)
-     (condition-case ()
-	 (progn
-	   (funcall (if (br-in-browser)
-			'find-tag 'find-tag-other-window)
-		    tag next)
-	   (message "Found definition for '%s'." tag))
-       (error
-	(if (not smart-c-use-lib-man)
-	    (progn (message "'%s' not found in '%s'." tag tags-file-name)
-		   (beep))
-	  (message
-	   "Checking if '%s' is an Objective-C library function..." tag)
-	  (if (smart-library-symbol tag)
-	      (progn
-		(message
-		 "Displaying Objective-C library man page for '%s'." tag)
-		(manual-entry tag))
-	    (message "'%s' not found in '%s' or Objective-C libraries."
-		     tag tags-file-name)
-	    (beep))))))))
+(defun smart-java-packages ()
+  "If point is on a `package' or `import' line, this tries to display the associated referent.
+Returns non-nil iff on such a line, even if the referent is not found.
+Look for packages in `smart-java-package-dirs'."
+  (let ((opoint (point)))
+    (beginning-of-line)
+    (if (looking-at smart-java-package-regexp)
+	(let ((keyword-type (buffer-substring
+			     (match-beginning 1) (match-end 1)))
+	      (referent (buffer-substring (match-beginning 2) (match-end 2)))
+	      (found)
+	      (subpath)
+	      dir-list path subfile)
+	  (goto-char opoint)
+	  (if (string-equal keyword-type "package")
+	      (let ((library-path (smart-java-library-path referent)))
+		(if library-path
+		    (hpath:find (expand-file-name 
+				 (hypb:replace-match-string
+				  "\\." referent (file-name-as-directory "") t)
+				 library-path))
+		  ;; Show the current directory, which should contain this package.
+		  (hpath:find default-directory)))
+	    ;; This is an `import' statement.  If it includes a *, show the
+	    ;; associated library directory, otherwise, show the specific
+	    ;; package.
+	    (if (string-match "\\.\\*" referent)
+		(setq subfile (substring referent 0 (match-beginning 0))
+		      subfile (hypb:replace-match-string
+			       "\\." subfile (file-name-as-directory "") t))
+	      (setq subpath (hypb:replace-match-string
+			     "\\." referent (file-name-as-directory "") t)
+		    subfile (concat subpath ".java")))
+	    ;;
+	    ;; Try to find the path containing referent.
+	    ;;
+	    ;; Search up the current directory tree for a possible matching
+	    ;; directory below which the referent library might live and add
+	    ;; this to smart-java-package-dirs for searching.
+	    (let ((library-path (smart-java-library-path referent)))
+	      (if library-path
+		  (setq dir-list (cons library-path smart-java-package-dirs))))
 
-;;; The following should be called only if the OO-Browser is available.
-;;;###autoload
-(defun smart-objc-oobr (&optional junk)
-  "Jumps to the definition of selected Objective-C construct via OO-Browser support.
-Optional JUNK is ignored.  Does nothing if the OO-Browser is not available.
-
-It assumes that its caller has already checked that the key was pressed in an
-appropriate buffer and has moved the cursor to the selected buffer.
-
-If key is pressed:
- (1) on a '#include' statement, the include file is displayed;
-     Look for include file in directory lists 'smart-c-cpp-include-dirs'
-     and 'smart-c-include-dirs'.
- (2) within a method declaration, its definition is displayed;
- (3) on a class name, the class definition is shown.
-
- (2) and (3) require that an OO-Browser Environment has been loaded with
-     the {M-x br-env-load RTN} command."
-
-  (interactive)
-  (objc-to-definition 'other-win))
-
-(defun smart-objc-at-tag-p ()
-  "Return Objective-C tag name that point is within, else nil."
-  (let* ((identifier-chars "_a-zA-Z0-9")
-	 (identifier
-	  (concat "\\([-+][ \t]*\\)?\\([_a-zA-Z][" identifier-chars "]*\\)")))
-    (save-excursion
-      (skip-chars-backward identifier-chars)
-      (if (looking-at identifier)
-	  (buffer-substring (match-beginning 2) (match-end 2))))))
-
-;;; ************************************************************************
-;;; Private functions
-;;; ************************************************************************
+	    (while dir-list
+	      (setq path (expand-file-name subfile (car dir-list))
+		    dir-list (if (setq found (file-exists-p path))
+				 nil
+			       (cdr dir-list))))
+	    (if (and (not found) subpath hyperb:microcruft-os-p)
+		;; Try .jav suffix.
+		(progn (setq subfile (concat subpath ".jav")
+			     dir-list smart-java-package-dirs)
+		       (while dir-list
+			 (setq path (expand-file-name subfile (car dir-list))
+			       dir-list (if (setq found (file-exists-p path))
+					    nil
+					  (cdr dir-list))))))
+	    ;;
+	    ;; If found, display file
+	    ;;
+	    (if found
+		(if (file-readable-p path)
+		    (hpath:find path)
+		  (beep)
+		  (message "(smart-java-packages):  `%s' unreadable." path))
+	      (beep)
+	      (message "(smart-java-packages):  `%s' not found." referent))
+	    path))
+      (goto-char opoint)
+      nil)))
 
 (defun smart-library-symbol (tag)
   "Return non-nil if TAG is a library symbol listed in cache of such symbols.
@@ -607,6 +853,25 @@
       (kill-buffer buf)
       found)))
 
+(defun smart-tags-display (tag next)
+  (if next (setq tag nil))
+  (let ((func (or (if (fboundp 'find-tag-internal) 'find-tag-internal)
+		  (if (fboundp 'find-tag-noselect) 'find-tag-noselect)))
+	;; For XEmacs
+	(tags-always-exact t)
+	;; For Emacs 19
+	(find-tag-tag-order (if (boundp 'find-tag-tag-order)
+				find-tag-tag-order)))
+    (if find-tag-tag-order
+	(if next nil (setq find-tag-tag-order '(tag-exact-match-p)))
+      ;; For InfoDock (XEmacs may also take this branch), force exact match.
+      (if (stringp tag) (setq tag (list tag))))
+    (if (and func (funcall func tag))
+	(hpath:display-buffer (current-buffer)))
+    ;; Signals an error if tag is not found which is caught by many callers
+    ;; of this function.
+    (find-tag tag)))
+
 ;;;###autoload
 (defun smart-tags-file-path (file)
   "Expand relative FILE name by looking it up in the nearest tags file.
@@ -628,8 +893,9 @@
       file))
 
 ;;;###autoload
-(defun smart-tags-file (curr-filename)
-  "Return appropriate tags file name for CURR-FILENAME or 'tags-file-name'."
+(defun smart-tags-file (curr-filename &optional name-of-tags-file)
+  "Return appropriate tags file name for CURR-FILENAME or `tags-file-name'.
+Optional NAME-OF-TAGS-FILE is the literal filename for which to look."
   (let ((path curr-filename)
 	(tags-file))
     (while (and
@@ -640,7 +906,7 @@
 	    (not (string-match ":?/\\'" path))
 	    ;; No tags file
 	    (not (file-exists-p
-		  (setq tags-file (expand-file-name "TAGS" path)))))
+		  (setq tags-file (expand-file-name (or name-of-tags-file "TAGS") path)))))
       (setq tags-file nil))
     (if (and (not tags-file)
 	     (stringp curr-filename)