diff lisp/packages/hyper-apropos.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 0293115a14e9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/packages/hyper-apropos.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,888 @@
+;;; hyper-apropos.el --- Hypertext emacs lisp documentation interface.
+
+;; Copyright (C) 1994, 1995 Tinker Systems and INS Engineering Corp.
+;; Copyright (C) 1995 Sun Microsystems.
+;; Copyright (C) 1996 Ben Wing.
+
+;; Maintainer: Jonathan Stigelman <Stig@hackvan.com>
+;; Keywords: lisp, tools, help, docs, matching
+
+;; This file is part of XEmacs.
+
+;; XEmacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2 of the License, or
+;; (at your option) any later version.
+;; 
+;; XEmacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+;; 
+;; You should have received a copy of the GNU General Public License
+;; along with XEmacs; if not, write to the Free Software
+;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;;; Synched up with: Not in FSF.
+
+;;; Commentary:
+
+;;  based upon emacs-apropos.el by Frank C. Guida <fcg@philabs.philips.com>
+;;
+;;  Rather than run apropos and print all the documentation at once,
+;;  I find it easier to view a "table of contents" first, then
+;;  get the details for symbols as you need them.
+;;
+;;  This version of apropos prints two lists of symbols matching the
+;;  given regexp:  functions/macros and variables/constants.
+;;
+;;  The user can then do the following:
+;;
+;;      - add an additional regexp to narrow the search
+;;      - display documentation for the current symbol
+;;      - find the tag for the current symbol
+;;      - show any keybindings if the current symbol is a command
+;;	- invoke functions
+;;	- set variables
+;;
+;;  An additional feature is the ability to search the current tags
+;;  table, allowing you to interrogate functions not yet loaded (this
+;;  isn't available with the standard package).
+;;
+;;  Mouse bindings and menus are provided for XEmacs.
+;;
+;; additions by Ben Wing <wing@666.com> July 1995:
+;; added support for function aliases, made programmer's apropos be the
+;; default, various other hacking.
+
+;;; Code:
+
+(or (fboundp 'pprint)
+    (progn (autoload 'pp "pp")
+	   (fset 'pprint 'pp)))
+;;(require 'tags "etags")
+
+;;;###autoload
+(defvar hypropos-show-brief-docs t
+  "*If non-nil, `hyper-apropos' will display some documentation in the
+\"*Hyper Apropos*\" buffer.  Setting this to nil will speed up searches.")
+
+(defvar hypropos-prettyprint-long-values t
+  "*If non-nil, then try to beautify the printing of very long values.")
+
+;; I changed this to true because I think it's more useful this way. --ben
+
+(defvar hypropos-programming-apropos t
+  "*If non-nil, then `hyper-apropos' takes a bit longer and generates more
+output.  If nil, then only functions that are interactive and variables that
+are user variables are found by `hyper-apropos'.")
+
+(defvar hypropos-prev-wconfig)
+
+;; #### - move this to subr.el
+(or (fboundp 'event-buffer)
+    (defun event-buffer (event)
+      "Returns the buffer associated with event, or nil."
+      (let ((win (event-window event)))
+	(and win (window-buffer win)))))
+
+(defmacro eval-in-buffer (buffer &rest forms)
+  "Evaluate FORMS in BUFFER."
+  (` (let ((_unwind_buf_ (current-buffer)))
+       (unwind-protect
+	   (progn (set-buffer (, buffer))
+		  (,@ forms))
+	 (set-buffer _unwind_buf_)))))
+(put 'eval-in-buffer 'lisp-indent-function 'defun)
+	 
+;; #### - move to faces.el
+(defmacro init-face (face &rest init-forms)
+  "Make a FACE if it doesn't already exist.  Then if it does not differ from
+the default face, execute INIT-FORMS to initialize the face.  While the
+init-forms are executing, the symbol `this' is bound to the face-object
+being initialized." 
+  (` (let ((this (make-face (, face))))	; harmless if the face is already there
+     (or (face-differs-from-default-p this)
+	 (, (cons 'progn init-forms))))))
+(put 'init-face 'lisp-indent-function 'defun)
+
+(init-face 'hyperlink
+  (copy-face 'bold this)
+  ;;(set-face-underline-p this nil) -- dog slow and ugly
+  (condition-case nil
+      (set-face-foreground this "blue")
+    (error nil)))
+(init-face 'documentation
+  (let* ((ff-instance (face-font-instance 'default))
+	(ff (and ff-instance (font-instance-name ff-instance))))
+    (cond ((and ff (string-match "courier" ff))
+	   ;; too wide unless you shrink it
+	   ;; (copy-face 'italic this) fugly.
+	   ;; (make-face-smaller this) fugly.
+	   ))
+    (condition-case nil
+	(set-face-foreground this "firebrick")
+      (error (copy-face 'italic this)))))
+
+;; mucking with the sizes of fonts (perhaps with the exception of courier or
+;; misc) is a generally losing thing to do.  Changing the size of 'clean'
+;; really loses, for instance...
+
+(init-face 'major-heading
+  (copy-face 'bold this)
+  (make-face-larger this)
+  (make-face-larger this))
+(init-face 'section-heading
+  (copy-face 'bold this)
+  (make-face-larger this))
+(init-face 'heading
+  (copy-face 'bold this))
+(init-face 'standout
+  (copy-face 'italic this))
+
+(init-face 'warning
+  (copy-face 'bold this)
+  (and (eq (device-type) 'x)
+       (eq (device-class) 'color)
+       (set-face-foreground this "red")))
+
+(defvar hypropos-help-map (let ((map (make-sparse-keymap)))
+			    (suppress-keymap map)
+			    (set-keymap-name map 'hypropos-help-map)
+			    ;; movement
+			    (define-key map " "     'scroll-up)
+			    (define-key map "b"     'scroll-down)
+			    (define-key map "/"     'isearch-forward)
+			    (define-key map "?"     'isearch-backward)
+			    ;; follow links
+			    (define-key map "\r"    'hypropos-get-doc)
+			    (define-key map "s"     'hypropos-set-variable)
+			    (define-key map "t"     'hypropos-find-tag)
+			    (define-key map "l"     'hypropos-last-help)
+			    (define-key map [button2] 'hypropos-mouse-get-doc)
+			    (define-key map [button3] 'hypropos-popup-menu)
+			    ;; for the totally hardcore...
+			    (define-key map "D"     'hypropos-disassemble)
+			    ;; administrativa
+			    (define-key map "a"     'hyper-apropos)
+			    (define-key map "n"     'hyper-apropos)
+			    (define-key map "q"     'hypropos-quit)
+			    map
+			    )
+  "Keybindings for both the *Hyper Help* buffer and the *Hyper Apropos* buffer")
+
+(defvar hypropos-map (let ((map (make-sparse-keymap)))
+		       (set-keymap-name map 'hypropos-map)
+		       (set-keymap-parents map (list hypropos-help-map))
+		       ;; slightly differrent scrolling...
+		       (define-key map " "     'hypropos-scroll-up)
+		       (define-key map "b"     'hypropos-scroll-down)
+		       ;; act on the current line...
+		       (define-key map "w"     'hypropos-where-is)
+		       (define-key map "i"     'hypropos-invoke-fn)
+		       (define-key map "s"     'hypropos-set-variable)
+		       ;; more administrativa...
+		       (define-key map "P"     'hypropos-toggle-programming-flag)
+		       (define-key map "k"     'hypropos-add-keyword)
+		       (define-key map "e"     'hypropos-eliminate-keyword)
+		       map
+		       )
+  "Keybindings for the *Hyper Apropos* buffer.
+This map inherits from `hypropos-help-map.'")
+
+(defvar hyper-apropos-mode-hook nil
+  "*User function run after hyper-apropos mode initialization.  Usage:
+\(setq hyper-apropos-mode-hook '(lambda () ... your init forms ...)).")
+
+;; ---------------------------------------------------------------------- ;;
+
+(defconst hypropos-junk-regexp "^Apropos\\|^Functions\\|^Variables\\|^$")
+
+(defvar hypropos-currently-showing nil)	; symbol documented in help buffer now
+(defvar hypropos-help-history nil)	; chain of symbols followed as links in
+					; help buffer
+(defvar hypropos-last-regexp nil)	; regex used for last apropos
+(defconst hypropos-apropos-buf "*Hyper Apropos*")
+(defconst hypropos-help-buf "*Hyper Help*")
+
+;;;###autoload
+(defun hyper-apropos (regexp toggle-apropos)
+  "Display lists of functions and variables matching REGEXP
+in buffer \"*Hyper Apropos*\".  If optional prefix arg is given, then the value
+of `hypropos-programming-apropos' is toggled for this search.
+See also `hyper-apropos-mode'."
+  (interactive "sList symbols matching regexp: \nP")
+  (or (memq major-mode '(hyper-apropos-mode hyper-help-mode))
+      (setq hypropos-prev-wconfig (current-window-configuration)))
+  (if (string= "" regexp)
+      (if (get-buffer hypropos-apropos-buf)
+	  (if toggle-apropos
+	      (hypropos-toggle-programming-flag)
+	    (message "Using last search results"))
+	(error "Be more specific..."))
+    (let (flist vlist)
+      (set-buffer (get-buffer-create hypropos-apropos-buf))
+      (setq buffer-read-only nil)
+      (erase-buffer)
+      (if toggle-apropos
+	  (set (make-local-variable 'hypropos-programming-apropos)
+	       (not (default-value 'hypropos-programming-apropos))))
+      (if (not hypropos-programming-apropos)
+	  (setq flist (apropos-internal regexp 'commandp)
+		vlist (apropos-internal regexp 'user-variable-p))
+	;; #### - add obsolete functions/variables here...
+	;; #### - 'variables' may be unbound !!!
+	(setq flist (apropos-internal regexp 'fboundp)
+	      vlist (apropos-internal regexp 'boundp)))
+      (insert-face (format "Apropos search for: %S\n\n" regexp) 'major-heading)
+      (insert-face "* = command (M-x) or user-variable.\n" 'documentation)
+      (insert-face "a = autoloaded, b = byte-compiled, i = internal, l = lambda, m = macro.\n\n" 'documentation)
+      (insert-face "Functions and Macros:\n\n" 'major-heading)
+      (hypropos-grok-functions flist)
+      (insert-face "\n\nVariables and Constants:\n\n" 'major-heading)
+      (hypropos-grok-variables vlist)
+      (goto-char (point-min))
+      ))
+  (switch-to-buffer hypropos-apropos-buf)
+  (hyper-apropos-mode regexp))
+
+(defun hypropos-toggle-programming-flag ()
+  (interactive)
+  (eval-in-buffer hypropos-apropos-buf
+    (set (make-local-variable 'hypropos-programming-apropos)
+	 (not hypropos-programming-apropos)))
+  (message "Re-running apropos...")
+  (hyper-apropos hypropos-last-regexp nil))
+
+(defun hypropos-grok-functions (fns)
+  (let (fn bind type)
+    (while (setq fn (car fns))
+      (setq bind (symbol-function fn)
+	    type (cond ((subrp bind) ?i)
+		       ((compiled-function-p bind) ?b)
+		       ((consp bind) (or (cdr
+					  (assq (car bind) '((autoload . ?a)
+							     (lambda . ?l)
+							     (macro . ?m))))
+					 ??))
+		       (t ? )))
+      (insert type (if (commandp fn) "* " "  "))
+      (insert-face (format "%-30S" fn) 'hyperlink)
+      (and hypropos-show-brief-docs
+	   (if (function-obsolete-p fn)
+	       (insert-face " - Obsolete." 'documentation)
+	     (let ((doc (documentation fn)))
+	       (if (not doc)
+		   (insert-face " - Not documented." 'documentation)
+		 (insert-face (concat " - "
+				      (substring doc 0
+						 (string-match "\n" doc)))
+			      'documentation)))))
+      (insert ?\n)
+      (setq fns (cdr fns))
+      )))
+
+(defun hypropos-grok-variables (vars)
+  (let (var userp)
+    (while (setq var (car vars))
+      (setq userp (user-variable-p var)
+	    vars (cdr vars))
+      (insert (if userp " * " "   "))
+      (insert-face (format "%-30S" var) 'hyperlink)
+      (and hypropos-show-brief-docs
+	   (if (variable-obsolete-p var)
+	       (insert-face " - Obsolete." 'documentation)
+	     (let ((doc (documentation-property var 'variable-documentation)))
+	       (if (not doc)
+		   (insert-face " - Not documented." 'documentation)
+		 (insert-face (concat " - "
+				      (substring doc (if userp 1 0)
+						 (string-match "\n" doc)))
+			      'documentation)))))
+      (insert ?\n)
+      )))
+
+;; ---------------------------------------------------------------------- ;;
+
+(defun hyper-apropos-mode (regexp)
+  "Improved apropos mode for displaying Emacs documentation.  Function and
+variable names are displayed in the buffer \"*Hyper Apropos*\".  
+
+Functions are preceded by a single character to indicates their types:
+    a = autoloaded, b = byte-compiled, i = internal, l = lambda, m = macro.
+Interactive functions are also preceded by an asterisk.
+Variables are preceded by an asterisk if they are user variables.
+
+General Commands:
+
+  	SPC	- scroll documentation or apropos window forward
+  	  b	- scroll documentation or apropos window backward
+	  k     - eliminate all hits that don't contain keyword
+	  n	- new search
+  	  /	- isearch-forward
+  	  q	- quit and restore previous window configuration
+  
+  Operations for Symbol on Current Line:
+  
+      	RET 	- toggle display of symbol's documentation
+		  (also on button2 in xemacs)
+  	  w     - show the keybinding if symbol is a command
+  	  i	- invoke function on current line
+  	  s	- set value of variable on current line
+	  t	- display the C or lisp source (find-tag)"
+  (delete-other-windows)
+  (setq mode-name "Hyper-Apropos"
+	major-mode 'hyper-apropos-mode
+	buffer-read-only t
+	truncate-lines t
+	hypropos-last-regexp regexp
+	modeline-buffer-identification (concat "Hyper Apropos: "
+					       "\"" regexp "\""))
+  (setq mode-motion-hook 'mode-motion-highlight-line)
+  (use-local-map hypropos-map)
+  (run-hooks 'hyper-apropos-mode-hook))
+
+;; ---------------------------------------------------------------------- ;;
+
+;;;###autoload
+(defun hyper-describe-variable (symbol)
+  "Hypertext drop-in replacement for `describe-variable'.
+See also `hyper-apropos' and `hyper-describe-function'."
+  ;; #### - perhaps a prefix arg should suppress the prompt...
+  (interactive 
+   (let* ((v (variable-at-point))
+          (val (let ((enable-recursive-minibuffers t))
+                 (completing-read
+		  (if v
+		      (format "Describe variable (default %s): " v)
+		    "Describe variable: ")
+		  obarray 'boundp t))))
+     (list (if (string= val "") v (intern-soft val)))))
+  (if (null symbol)
+      (message "Sorry, nothing to describe.")
+    (or (memq major-mode '(hyper-apropos-mode hyper-help-mode))
+	(setq hypropos-prev-wconfig (current-window-configuration)))
+    (hypropos-get-doc symbol t)))
+
+;;;###autoload
+(defun hyper-describe-function (symbol)
+  "Hypertext replacement for `describe-function'.  Unlike `describe-function'
+in that the symbol under the cursor is the default if it is a function.
+See also `hyper-apropos' and `hyper-describe-variable'."
+  ;; #### - perhaps a prefix arg should suppress the prompt...
+  (interactive
+   (let (fn val)
+     (setq fn (hypropos-this-symbol))	; symbol under point
+     (or (fboundp fn)
+	 (setq fn (function-called-at-point)))
+     (setq val (let ((enable-recursive-minibuffers t))
+		 (completing-read
+		  (if fn 
+		      (format "Describe function (default %s): " fn)
+		    "Describe function: ")
+		  obarray 'fboundp t)))
+     (list (if (equal val "") fn (intern-soft val)))))
+  (if (null symbol)
+      (message "Sorry, nothing to describe.")
+    (or (memq major-mode '(hyper-apropos-mode hyper-help-mode))
+	(setq hypropos-prev-wconfig (current-window-configuration)))
+    (hypropos-get-doc symbol t)))
+
+(defun hypropos-last-help (arg)
+  "Go back to the last symbol documented in the *Hyper Help* buffer."
+  (interactive "P")
+  (let ((win (get-buffer-window hypropos-help-buf))
+	(n (prefix-numeric-value arg)))
+    (cond ((and (not win) (not arg))
+	   ;; don't alter the help-history, just redisplay
+	   )
+	  ((<= (length hypropos-help-history) n)
+	   ;; go back as far as we can...
+	   (setcdr (nreverse hypropos-help-history) nil))
+	  (t
+	   (setq hypropos-help-history (nthcdr n hypropos-help-history))))
+    (hypropos-get-doc (car hypropos-help-history) t)))
+
+(defun hypropos-get-doc (&optional symbol force type)
+  ;; #### - update this docstring
+  "Toggle display of documentation for the symbol on the current line."
+  ;; SYMBOL is the symbol to document.  FORCE, if non-nil, means to
+  ;; regenerate the documentation even if it already seems to be there.  And
+  ;; TYPE, if present, forces the generation of only variable documentation
+  ;; or only function documentation.  Normally, if both are present, then
+  ;; both will be generated.
+  ;;
+  ;; TYPES TO IMPLEMENT: obsolete face
+  ;;
+  (interactive)
+  (or symbol
+      (setq symbol (hypropos-this-symbol)))
+  (or type
+      (setq type '(function variable face)))
+  (if (and (eq hypropos-currently-showing symbol)
+	   (get-buffer hypropos-help-buf)
+	   (get-buffer-window hypropos-help-buf)
+	   (not force))
+      ;; we're already displaying this help, so toggle its display.
+      (delete-windows-on hypropos-help-buf)
+    ;; OK, we've got to refresh and display it...
+    (or (eq symbol (car hypropos-help-history))
+	(setq hypropos-help-history
+	      (if (eq major-mode 'hyper-help-mode)
+		  ;; if we're following a link in the help buffer, then
+		  ;; record that in the help history.
+		  (cons symbol hypropos-help-history)
+		;; otherwise clear the history because it's a new search.
+		(list symbol))))
+    (save-excursion
+      (set-buffer (get-buffer-create hypropos-help-buf))
+      (setq buffer-read-only nil)
+      (erase-buffer)
+      (let ((standard-output (current-buffer))
+	    ok beg desc
+	    ftype macrop fndef
+	    keys val doc
+	    obsolete aliases alias-desc)
+	(insert-face (format "`%s'\n\n" symbol) 'major-heading)
+	(and (memq 'function type)
+	     (fboundp symbol)
+	     (progn 
+	       (setq ok t
+		     fndef (symbol-function symbol))
+	       (while (symbolp fndef)
+		 (setq aliases (cons fndef aliases))
+		 (setq fndef (symbol-function fndef)))
+	       (if (eq 'macro (car-safe fndef))
+		   (setq macrop t
+			 fndef (cdr fndef)))
+	       (setq aliases (nreverse aliases))
+	       ;; #### - the gods of internationalization shall strike me down!
+	       (while aliases
+		 (if alias-desc
+		     (setq alias-desc (concat alias-desc ",\nwhich is ")))
+		 (setq alias-desc (concat alias-desc
+					  (format "an alias for `%s'"
+						  (car aliases))))
+		 (setq aliases (cdr aliases)))
+	       (setq ftype (cond ((subrp fndef)                   'subr)
+				 ((compiled-function-p fndef)     'bytecode)
+				 ((eq (car-safe fndef) 'autoload) 'autoload)
+				 ((eq (car-safe fndef) 'lambda)	  'lambda))
+		     desc (concat (if (commandp symbol) "interactive ")
+				  (cdr (assq ftype
+					     '((subr     . "built-in ")
+					       (bytecode . "compiled Lisp ")
+					       (autoload . "autoloaded Lisp ")
+					       (lambda   . "Lisp "))))
+				  (if macrop "macro" "function")
+				  ))
+	       (if alias-desc
+		   (setq desc (concat alias-desc
+				      (if (memq (aref desc 0)
+						'(?a ?e ?i ?o ?u))
+					  ", an " ", a ")
+				      desc)))
+	       (aset desc 0 (upcase (aref desc 0))) ; capitalize
+	       (insert-face desc 'section-heading)
+	       (and (eq ftype 'autoload)
+		    (insert (format ", (autoloaded from \"%s\")"
+				    (nth 1 fndef))))
+	       ;; #### - should also show local binding in some other
+	       ;; buffer so that this function can be used in place of
+	       ;; describe-function and describe-variable.
+	       (if (setq keys (where-is-internal symbol (current-global-map)
+						 nil nil nil))
+		   (insert (format ", (globally bound to %s)"
+				   (mapconcat
+				    #'(lambda (x)
+					(format "\"%s\""
+						(key-description x)))
+				    (sort keys #'(lambda (x y)
+						   (< (length x) (length y))))
+				    ", "))))
+	       (insert ":\n\n")
+	       (setq beg (point)
+		     doc (or (documentation symbol) "function not documented"))
+	       (insert-face "arguments: " 'heading)
+	       (cond ((eq ftype 'lambda)
+		      (princ (or (nth 1 fndef) "()")))
+		     ((eq ftype 'bytecode)
+		      (princ (or (if (fboundp 'compiled-function-arglist)
+				     (compiled-function-arglist fndef)
+				   (aref fndef 0)) "()")))
+		     ((and (eq ftype 'subr)
+			   (string-match
+			    "[\n\t ]*\narguments: ?\\((.*)\\)\n?\\'"
+			    doc))
+		      (insert (substring doc
+					 (match-beginning 1)
+					 (match-end 1)))
+		      (setq doc (substring doc 0 (match-beginning 0))))
+		     (t (princ "[not available]")))
+	       (insert "\n\n")
+	       (let ((new
+		      ;; cookbook from bytecomp.el
+		      (get symbol 'byte-obsolete-info)))
+		 (and new
+		      (insert-face
+		       (format "%s is an obsolete function; %s\n\n" symbol
+			       (if (stringp (car new))
+				   (car new)
+				 (format "use %s instead." (car new))))
+		       'warning)))
+	       (insert-face doc 'documentation)
+	       (indent-rigidly beg (point) 1)
+	       (insert"\n\n")
+	       ))
+	(and (memq 'variable type)
+	     (boundp symbol)
+	     (progn 
+	       (setq ok t)
+	       (insert-face (if (user-variable-p symbol)
+				"User variable"
+			      "Variable")
+			    'section-heading)
+	       (and (local-variable-p symbol nil t)
+		    (insert ", local when set"))
+	       (insert ":\n\n")
+	       (setq beg (point)
+		     val (prin1-to-string (symbol-value symbol))
+		     doc (or (documentation-property
+			      symbol 'variable-documentation)
+			     "variable not documented"))
+	       
+	       (let ((ob (get symbol 'byte-obsolete-variable)))
+		 (setq obsolete
+		       (and ob (format "%s is an obsolete variable; %s\n\n"
+				       symbol
+				       (if (stringp ob)
+					   ob
+					 (format "use %s instead." ob))))))
+	       ;; generally, the value of the variable is short and the
+	       ;; documentation of the variable long, so it's desirable
+	       ;; to see all of the value and the start of the
+	       ;; documentation.  Some variables, though, have huge and
+	       ;; nearly meaningless values that force you to page
+	       ;; forward just to find the doc string.  That is
+	       ;; undesirable.
+	       (if (< (length val) 69)	; 80 cols.  docstrings assume this.
+		   (progn (insert-face "value: " 'heading)
+			  (insert (format "%s\n\n" val))
+			  (and obsolete (insert-face obsolete 'warning))
+			  (insert-face doc 'documentation))
+		 (insert "(see below for value)\n\n")
+		 (and obsolete (insert-face obsolete 'warning))
+		 (insert-face doc 'documentation)
+		 (insert "\n\n")
+		 (insert-face "value: " 'heading)
+		 (if hypropos-prettyprint-long-values
+		     (let ((pp-print-readably nil))
+		       (pprint (symbol-value symbol)))
+		   (insert val)))
+	       (indent-rigidly beg (point) 2)
+	       ))
+	(and (memq 'face type)
+	     (find-face symbol)
+	     (progn
+	       (setq ok t)
+	       ;; #### - add some code here
+	       (insert "Face documentation is \"To be implemented.\"\n\n")
+	       )
+	     )
+	(or ok (insert-face "symbol is not currently bound" 'heading)))
+      (goto-char (point-min)) 
+      ;; pop up window and shrink it if it's wasting space
+      (shrink-window-if-larger-than-buffer
+       (display-buffer (current-buffer))) 
+      (hyper-help-mode))    )
+  (setq hypropos-currently-showing symbol))
+
+; -----------------------------------------------------------------------------
+
+(defun hyper-help-mode ()
+  "Major mode for hypertext XEmacs help.  In this mode, you can quickly
+follow links between back and forth between the documentation strings for
+different variables and functions.  Common commands:
+
+\\{hypropos-help-map}"
+  (setq mode-motion-hook 'hypropos-highlight-lisp-symbol
+	buffer-read-only t
+	major-mode	     'hyper-help-mode
+	mode-name	     "Hyper-Help")
+  (set-syntax-table emacs-lisp-mode-syntax-table)
+  (use-local-map hypropos-help-map))
+
+(defun hypropos-highlight-lisp-symbol (event)
+  ;; mostly copied from mode-motion-highlight-internal
+  (let* ((window (event-window event))
+	 (buffer (and window (window-buffer window)))
+	 (point (and buffer (event-point event)))
+	 st en sym highlight-p)
+    (if buffer
+	(progn
+	  (set-buffer buffer)
+	  (if point
+	      (save-excursion
+		(goto-char point)
+		(setq st (save-excursion
+			   (skip-syntax-backward "w_")
+			   (skip-chars-forward "`")
+			   (point))
+		      en (save-excursion
+			   (goto-char st)
+			   (skip-syntax-forward "w_")
+			   (skip-chars-backward ".")
+			   (point))
+		      sym (and (not (eq st en))
+			       (intern-soft (buffer-substring st en)))
+		      highlight-p (and sym
+				       (or (boundp sym)
+					   (fboundp sym))))
+		(if highlight-p
+		    (if mode-motion-extent
+		      (set-extent-endpoints mode-motion-extent st en)
+		    (setq mode-motion-extent (make-extent st en))
+		    (set-extent-property mode-motion-extent 'highlight t))
+		  (and mode-motion-extent
+			 (progn (delete-extent mode-motion-extent)
+				(setq mode-motion-extent nil)))
+		  ))
+	    ;; not over text; zero the extent.
+	    (if (and mode-motion-extent (extent-buffer mode-motion-extent)
+		     (not (eq (extent-start-position mode-motion-extent)
+			      (extent-end-position mode-motion-extent))))
+		(set-extent-endpoints mode-motion-extent 1 1)))))))
+
+
+;; ---------------------------------------------------------------------- ;;
+
+(defun hypropos-scroll-up ()
+  "Scroll up the \"*Hyper Help*\" buffer if it's visible, or scroll this window up."
+  (interactive)
+  (let ((win (get-buffer-window hypropos-help-buf))
+	(owin (selected-window)))
+    (if win
+	(progn
+	  (select-window win)
+	  (condition-case nil
+	       (scroll-up nil)
+	      (error (goto-char (point-max))))
+	  (select-window owin))
+      (scroll-up nil))))
+
+(defun hypropos-scroll-down ()
+  "Scroll down the \"*Hyper Help*\" buffer if it's visible, or scroll this window down."
+  (interactive)
+  (let ((win (get-buffer-window hypropos-help-buf))
+	(owin (selected-window)))
+    (if win
+	(progn
+	  (select-window win)
+	  (condition-case nil
+	       (scroll-down nil)
+	      (error (goto-char (point-max))))
+	  (select-window owin))
+      (scroll-down nil))))
+
+;; ---------------------------------------------------------------------- ;;
+
+(defun hypropos-mouse-get-doc (event)
+  "Get the documentation for the symbol the mouse is on."
+  (interactive "e")
+  (mouse-set-point event)
+  (save-excursion
+    (let ((symbol (hypropos-this-symbol)))
+      (if symbol
+	  (hypropos-get-doc symbol)
+	(error "Click on a symbol")))))
+
+;; ---------------------------------------------------------------------- ;;
+
+(defun hypropos-add-keyword (pattern)
+  "Use additional keyword to narrow regexp match.
+Deletes lines which don't match PATTERN."
+  (interactive "sAdditional Keyword: ")
+  (save-excursion
+    (goto-char (point-min))
+    (let (buffer-read-only)
+      (keep-lines (concat pattern "\\|" hypropos-junk-regexp))
+      )))
+
+(defun hypropos-eliminate-keyword (pattern)
+  "Use additional keyword to eliminate uninteresting matches.
+Deletes lines which match PATTERN."
+  (interactive "sKeyword to eliminate: ")
+  (save-excursion
+    (goto-char (point-min))
+    (let (buffer-read-only)
+      (flush-lines pattern))
+      ))
+
+;; ---------------------------------------------------------------------- ;;
+
+(defun hypropos-this-symbol ()
+  (save-excursion
+    (cond ((eq major-mode 'hyper-apropos-mode)
+	   (beginning-of-line)
+	   (if (looking-at hypropos-junk-regexp)
+	       nil
+	     (forward-char 3)
+	     (read (point-marker))))
+	  (t
+	   (let* ((st (progn
+			(skip-syntax-backward "w_")
+			;; !@(*$^%%# stupid backquote implementation!!!
+			(skip-chars-forward "`")
+			(point)))
+		  (en (progn
+			(skip-syntax-forward "w_")
+			(skip-chars-backward ".")
+			(point))))
+	     (and (not (eq st en))
+		  (intern-soft (buffer-substring st en))))))))
+
+(defun hypropos-where-is (symbol)
+  "Find keybinding for symbol on current line."
+  (interactive (list (hypropos-this-symbol)))
+  (where-is symbol))
+
+(defun hypropos-invoke-fn (fn)
+  "Interactively invoke the function on the current line."
+  (interactive (list (hypropos-this-symbol)))
+  (cond ((not (fboundp fn))
+	 (error "%S is not a function" fn))
+	(t (call-interactively fn))))
+
+;;;###autoload
+(defun hypropos-set-variable (var val)
+  "Interactively set the variable on the current line."
+  (interactive
+   (let ((var (save-excursion
+		(and (eq major-mode 'hypropos-help-mode)
+		     (goto-char (point-min)))
+		(hypropos-this-symbol))))
+     (or (boundp var)
+	 (setq var (completing-read "Set variable: "
+				    obarray 'boundp t)))
+     (hypropos-get-doc var t)
+     (list var
+	   (let ((prop (get var 'variable-interactive))
+		 (print-readably t)
+		 (val (symbol-value var)))
+	     (if prop
+		 (call-interactively (list 'lambda '(arg)
+					   (list 'interactive prop)
+					   'arg))
+	       (eval-minibuffer
+		(format "Set `%s' to value (evaluated): " var)
+		(format (if (or (consp val)
+				(and (symbolp val)
+				     (not (memq val '(t nil)))))
+			    "'%s" "%s")
+			(prin1-to-string val))))))
+     ))
+  (set var val)
+  (hypropos-get-doc var t))
+
+;; ---------------------------------------------------------------------- ;;
+
+(defun hypropos-find-tag (&optional tag-name)
+  "Find the tag for the symbol on the current line in other window.  In
+order for this to work properly, the variable `tag-table-alist' or
+`tags-file-name' must be set so that a TAGS file with tags for the emacs
+source is found for the \"*Hyper Apropos*\" buffer."
+  (interactive)
+  ;; there ought to be a default tags file for this...
+  (or tag-name (setq tag-name (symbol-name (hypropos-this-symbol))))
+  (find-tag-other-window (list tag-name)))
+
+;; ---------------------------------------------------------------------- ;;
+
+(defun hypropos-disassemble (sym)
+  "Disassemble FUN if it is byte-coded.  If it's a lambda, prettyprint it."
+  (interactive (list (hypropos-this-symbol)))
+  (let ((fun sym) (trail nil) macrop)
+    (while (and (symbolp fun) (not (memq fun trail)))
+      (setq trail (cons fun trail)
+	    fun (symbol-function fun)))
+    (and (symbolp fun)
+	 (error "Loop detected in function binding of `%s'" fun))
+    (setq macrop (and  (consp fun)
+		       (eq 'macro (car fun))))
+    (cond ((compiled-function-p (if macrop (cdr fun) fun))
+	   (disassemble fun)
+	   (set-buffer "*Disassemble*")
+	   (goto-char (point-min))
+	   (forward-sexp 2)
+	   (insert (format " for function `%S'" sym))
+	   )
+	  ((consp fun)
+	   (with-output-to-temp-buffer "*Disassemble*"
+	     (pprint (if macrop
+			 (cons 'defmacro (cons sym (cdr (cdr fun))))
+		       (cons 'defun (cons sym (cdr fun))))))
+	   (set-buffer "*Disassemble*")
+	   (emacs-lisp-mode))
+	  ((or (vectorp fun) (stringp fun))
+	   ;; #### - do something fancy here
+	   (with-output-to-temp-buffer "*Disassemble*"
+	     (princ (format "%s is a keyboard macro:\n\n\t" sym))
+	     (prin1 fun)))
+	  (t
+	   (error "Sorry, cannot disassemble `%s'" sym)))))
+
+;; ---------------------------------------------------------------------- ;;
+
+(defun hypropos-quit ()
+  (interactive)
+  "Quit Hyper Apropos and restore original window config."
+  (let ((buf (get-buffer hypropos-apropos-buf)))
+    (and buf (bury-buffer buf)))
+  (set-window-configuration hypropos-prev-wconfig))
+
+;; ---------------------------------------------------------------------- ;;
+
+;;;###autoload
+(defun hypropos-popup-menu (event)
+  (interactive "e")
+  (mouse-set-point event)
+  (let* ((sym (hypropos-this-symbol))
+	 (notjunk (not (null sym)))
+	 (command-p (commandp sym))
+	 (variable-p (and sym (boundp sym)))
+	 (function-p (fboundp sym))
+	 (apropos-p (eq 'hyper-apropos-mode
+			(save-excursion (set-buffer (event-buffer event))
+					major-mode)))
+	 (name (if sym (symbol-name sym) ""))
+	 (hypropos-menu
+	  (delete
+	   nil
+	   (list (concat "Hyper-Help: " name)
+	    (vector "Display documentation" 'hypropos-get-doc   notjunk)
+	    (vector "Set variable"	'hypropos-set-variable	variable-p)
+	    (vector "Show keys for"     'hypropos-where-is      command-p)
+	    (vector "Invoke command"	'hypropos-invoke-fn	command-p)
+	    (vector "Find tag"		'hypropos-find-tag	notjunk)
+	    (and apropos-p
+		 ["Add keyword..." hypropos-add-keyword	t])
+	    (and apropos-p
+		 ["Eliminate keyword..." hypropos-eliminate-keyword  t])
+	    (if apropos-p
+		["Programmers' Apropos" hypropos-toggle-programming-flag
+		 :style toggle :selected hypropos-programming-apropos]
+	      ["Programmers' Help" hypropos-toggle-programming-flag
+	       :style toggle :selected hypropos-programming-apropos])
+	    (and hypropos-programming-apropos
+		 (vector "Disassemble function"
+			 'hypropos-disassemble
+			 function-p))
+	    ["Help"                     describe-mode           t]
+	    ["Quit"			hypropos-quit		t]
+	    ))))
+    (popup-menu hypropos-menu)))
+
+(provide 'hyper-apropos)
+
+;; end of hyper-apropos.el
+