help-mode+.el

Download Git Homepage

;;; help-mode+.el --- Extensions to `help-mode.el'
;;
;; Filename: help-mode+.el
;; Description: Extensions to `help-mode.el'
;; Author: Drew Adams
;; Maintainer: Drew Adams (concat "drew.adams" "@" "oracle" ".com")
;; Copyright (C) 2004-2019, Drew Adams, all rights reserved.
;; Created: Sat Nov 06 15:14:12 2004
;; Version: 0
;; Package-Requires: ()
;; Last-Updated: Thu Oct  8 12:32:36 2020 (-0700)
;;           By: dradams
;;     Update #: 227
;; URL: https://www.emacswiki.org/emacs/download/help-mode%2b.el
;; Doc URL: https://emacswiki.org/emacs/HelpPlus
;; Keywords: help
;; Compatibility: GNU Emacs: 22.x, 23.x, 24.x, 25.x, 26.x
;;
;; Features that might be required by this library:
;;
;;   `button', `cl-lib', `help-mode', `macroexp'.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Commentary:
;;
;;    Extensions to `help-mode.el'
;;
;;
;;  ***** NOTE: The following functions defined in `help-mode.el'
;;              have been REDEFINED HERE:
;;
;;  `help-make-xrefs' - Put symbol clause first, so cross-xref links
;;                      show doc for both fun and var, if available.
;;  `help-mode'       - If `one-window-p', then delete Help frame.
;;  `help-xref-on-pp' - Library names are buttonized.
;;
;;
;;  Put this in your initialization file (`~/.emacs'):
;;
;;    (require 'help-mode+)
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Change Log:
;;
;; 2020/10/08 dadams
;;     Added define-button-type for help-symbol, to show C-h S binding.
;; 2019/11/17 dadams
;;     help-make-xrefs: Typo for help-xref-button 7: help-character-set -> help-input-method.
;;                      Added clause for describe-symbol-backends, if defined (Emacs 25+).
;; 2018/01/01 dadams
;;     help-make-xrefs:
;;       Bug #12686: Promote type tests after cond tests to be inside them with the match tests.
;; 2014/01/17 dadams
;;     Do not redefine help-mode for Emacs 24+.
;; 2012/10/25 dadams
;;     Added redefinition of help-make-xrefs.
;; 2011/01/04 dadams
;;     Removed autoload cookie from non-interactive function.
;; 2007/12/14 dadams
;;     Added redefinition of help-mode.
;;     Removed pop-to-help-buffer and help-origin-buffer to (new) help+.el.
;; 2006/07/11 dadams
;;     Added: help-origin-buffer, pop-to-help-toggle.  Bound latter to C-h C-o.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; This program 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, or (at your option)
;; any later version.

;; This program 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 this program; see the file COPYING.  If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Code:

(require 'help-mode)

;; Quiet the byte-compiler.
(defvar help-forward-label)
(defvar help-xref-override-view-map)

;;;;;;;;;;;;;;;;;;;;;;;;


;; REPLACES ORIGINAL IN `help-mode.el'.
;;
;; Deletes frame if `one-window-p'.
;;
;;;###autoload
(when (< emacs-major-version 24)        ; Emacs 24+ does not use `view-mode', so no need.
  (defun help-mode ()
    "Major mode for viewing help text and navigating references in it.
Entry to this mode runs the normal hook `help-mode-hook'.
Commands:
\\{help-mode-map}"
    (interactive)
    (kill-all-local-variables)
    (use-local-map help-mode-map)
    (setq mode-name   "Help"
          major-mode  'help-mode)
    (view-mode)
    (make-local-variable 'view-no-disable-on-exit)
    (setq view-no-disable-on-exit  t
          view-exit-action         (lambda (buffer)
                                     (or (window-minibuffer-p (selected-window))
                                         (when (eq (window-buffer) (get-buffer "*Help*"))
                                           (if (one-window-p t)
                                               (delete-frame)
                                             (delete-window))))))
    (run-mode-hooks 'help-mode-hook)))


;; REPLACES ORIGINAL IN `help-mode.el'.
;;
;; Show all doc possible for a symbol that is any 2 or 3 of fn, var, and face.
;; See Emacs bug #12686.
;;
;; Also a partial fix for Emacs bug #24842.
;;
;;;###autoload
(defun help-make-xrefs (&optional buffer)
  "Parse and hyperlink documentation cross-references in the given BUFFER.

Find cross-reference information in a buffer and activate such cross
references for selection with `help-follow'.  Cross-references have
the canonical form `...'  and the type of reference may be
disambiguated by the preceding word(s) used in
`help-xref-symbol-regexp'.  Faces only get cross-referenced if
preceded or followed by the word `face'.  Variables without
variable documentation do not get cross-referenced, unless
preceded by the word `variable' or `option'.

If the variable `help-xref-mule-regexp' is non-nil, find also
cross-reference information related to multilingual environment
\(e.g., coding-systems).  This variable is also used to disambiguate
the type of reference as the same way as `help-xref-symbol-regexp'.

A special reference `back' is made to return back through a stack of
help buffers.  Variable `help-back-label' specifies the text for
that."
  (interactive "b")
  (with-current-buffer (or buffer  (current-buffer))
    (save-excursion
      (goto-char (point-min))
      ;; Skip the header-type info, though it might be useful to parse
      ;; it at some stage (e.g. "function in `library'").
      (forward-paragraph)
      (let ((old-modified  (buffer-modified-p)))
        (let ((stab               (syntax-table))
              (case-fold-search   t)
              (inhibit-read-only  t))
          (set-syntax-table emacs-lisp-mode-syntax-table)
          ;; The following should probably be abstracted out.
          (unwind-protect
               (progn
                 ;; Info references
                 (save-excursion
                   (while (re-search-forward help-xref-info-regexp nil t)
                     (let ((data  (match-string 2)))
                       (save-match-data
                         (unless (string-match "^([^)]+)" data)
                           (setq data  (concat "(emacs)" data)))
                         (setq data ;; possible newlines if para filled
                               (replace-regexp-in-string "[ \t\n]+" " " data t t)))
                       (help-xref-button 2 'help-info data))))
                 ;; URLs
                 (save-excursion
                   (while (re-search-forward help-xref-url-regexp nil t)
                     (let ((data  (match-string 1)))
                       (help-xref-button 1 'help-url data))))
                 ;; Mule related keywords.  Do this before trying
                 ;; `help-xref-symbol-regexp' because some of Mule
                 ;; keywords have variable or function definitions.

                 ;; $$$$$ Should I do it here too?  (Have not done so yet.)
                 (when help-xref-mule-regexp
                   (save-excursion
                     (while (re-search-forward help-xref-mule-regexp nil t)
                       (let* ((data  (match-string 7))
                              (sym   (intern-soft data)))
                         (cond ((match-string 3) ; coding system
                                (and sym
                                     (coding-system-p sym)
                                     (help-xref-button 6 'help-coding-system sym)))
                               ((match-string 4) ; input method
                                (and (assoc data input-method-alist)
                                     (help-xref-button 7 'help-input-method data)))
                               ((or (match-string 5)  (match-string 6)) ; charset
                                (and sym
                                     (charsetp sym)
                                     (help-xref-button 7 'help-character-set sym)))
                               ((assoc data input-method-alist)
                                (help-xref-button 7 'help-input-method data))
                               ((and sym  (coding-system-p sym))
                                (help-xref-button 7 'help-coding-system sym))
                               ((and sym  (charsetp sym))
                                (help-xref-button 7 'help-character-set sym)))))))
                 ;; Quoted symbols
                 (save-excursion
                   (while (re-search-forward help-xref-symbol-regexp nil t)
                     (let* ((data  (match-string 8))
                            (sym   (intern-soft data)))
                       (when sym
                         (cond ((or (and (or (boundp sym) ; var & fn
                                             (get sym 'variable-documentation))
                                         (fboundp sym))
                                    (and (fboundp sym) ; fn & face
                                         (facep sym))
                                    (and (or (boundp sym) ; var & face
                                             (get sym 'variable-documentation))
                                         (facep sym)))
                                ;; Var, function, or face -- doc all, if possible.
                                (help-xref-button 8 'help-symbol sym))
                               ((and (match-string 3) ; `variable' &c
                                     (or (boundp sym) ; `variable' doesn't ensure
                                        ; it's actually bound
                                         (get sym 'variable-documentation)))
                                (help-xref-button 8 'help-variable sym))
                               ((and (match-string 4) ; `function' &c
                                     (fboundp sym))   ; similarly
                                (help-xref-button 8 'help-function sym))
                               ((and (match-string 5) ; `face'
                                     (facep sym))
                                (help-xref-button 8 'help-face sym))
                               ((match-string 6)) ; nothing for `symbol'
                               ((match-string 7)
                                ;; this used:
                                ;; #'(lambda (arg)
                                ;;     (let ((location  (find-function-noselect arg)))
                                ;;       (pop-to-buffer (car location))
                                ;; 	(goto-char (cdr location))))
                                (help-xref-button 8 'help-function-def sym))
                               ((and (boundp 'describe-symbol-backends) ; Emacs 25+
                                     (fboundp 'cl-some)
                                     (cl-some (lambda (x) (funcall (nth 1 x) sym))
                                     describe-symbol-backends))
                                (help-xref-button 8 'help-symbol sym))
                               ((and (facep sym)
                                     (save-match-data (looking-at "[ \t\n]+face\\W")))
                                (help-xref-button 8 'help-face sym))
                               ((and
                                 (or (boundp sym)  (get sym 'variable-documentation))
                                 (or (documentation-property sym 'variable-documentation)
                                     (if (or (> emacs-major-version 24)
                                             (and (= emacs-major-version 24)
                                                  (> emacs-minor-version 2)))
                                         (documentation-property (indirect-variable sym)
                                                                 'variable-documentation)
                                       (condition-case nil
                                           (documentation-property (indirect-variable sym)
                                                                   'variable-documentation)
                                         (cyclic-variable-indirection nil)))))
                                (help-xref-button 8 'help-variable sym))
                               ((fboundp sym)
                                (help-xref-button 8 'help-function sym)))))))
                 ;; An obvious case of a key substitution:
                 (save-excursion
                   (while (re-search-forward
                           ;; Assume command name is only word and symbol
                           ;; characters to get things like `use M-x foo->bar'.
                           ;; Command required to end with word constituent
                           ;; to avoid `.' at end of a sentence.
                           "\\<M-x\\s-+\\(\\sw\\(\\sw\\|\\s_\\)*\\sw\\)" nil t)
                     (let ((sym  (intern-soft (match-string 1))))
                       (when (fboundp sym) (help-xref-button 1 'help-function sym)))))
                 ;; Look for commands in whole keymap substitutions:
                 (save-excursion
                   ;; Make sure to find the first keymap.
                   (goto-char (point-min))
                   ;; Find a header and the column at which the command
                   ;; name will be found.

                   ;; If the keymap substitution isn't the last thing in
                   ;; the doc string, and if there is anything on the same
                   ;; line after it, this code won't recognize the end of it.
                   (while (re-search-forward "^key +binding\n\\(-+ +\\)-+\n\n"
                                             nil t)
                     (let ((col  (- (match-end 1) (match-beginning 1))))
                       (while (and (not (eobp))
                                   ;; Stop at a pair of blank lines.
                                   (not (looking-at "\n\\s-*\n")))
                         ;; Skip a single blank line.
                         (and (eolp)  (forward-line))
                         (end-of-line)
                         (skip-chars-backward "^ \t\n")
                         (when (and (>= (current-column) col)
                                    (looking-at "\\(\\sw\\|\\s_\\)+$"))
                           (let ((sym  (intern-soft (match-string 0))))
                             (when (fboundp sym) (help-xref-button 0 'help-function sym))))
                         (forward-line))))))
            (set-syntax-table stab))
          ;; Delete extraneous newlines at the end of the docstring
          (goto-char (point-max))
          (while (and (not (bobp))  (bolp)) (delete-char -1))
          (insert "\n")
          (when (or help-xref-stack  (and (boundp 'help-xref-forward-stack)
                                          help-xref-forward-stack))
            (insert "\n"))
          ;; Make a back-reference in this buffer if appropriate.
          (when help-xref-stack
            (help-insert-xref-button help-back-label 'help-back
                                     (current-buffer)))
          ;; Make a forward-reference in this buffer if appropriate.
          (when (and (boundp 'help-xref-forward-stack)  help-xref-forward-stack)
            (when help-xref-stack
              (insert "\t"))
            (help-insert-xref-button help-forward-label 'help-forward
                                     (current-buffer)))
          (when (or help-xref-stack  (and (boundp 'help-xref-forward-stack)
                                          help-xref-forward-stack))
            (insert "\n")))
        (when (< emacs-major-version 24)
          ;; View mode steals RET from us.
          (set (make-local-variable 'minor-mode-overriding-map-alist)
               (list (cons 'view-mode help-xref-override-view-map))))
        (when (or (> emacs-major-version 24)
                  (and (= emacs-major-version 24)  (> emacs-minor-version 2)))
          (set-buffer-modified-p old-modified))))))


;; REPLACES ORIGINAL IN `help-mode.el'.

;; Buttonizes names of libraries also.
;; To see the effect, try `C-h v features', and click on a library name.
;;
;; 2006-01-20: This no longer works, because the call to this function
;; from `describe-variable' was commented out in `help-fns.el'.
;;
(defun help-xref-on-pp (from to)
  "Add xrefs for symbols in `pp's output between FROM and TO."
  (if (> (- to from) 5000)
      nil
    (with-syntax-table emacs-lisp-mode-syntax-table
      (save-excursion
        (save-restriction
          (narrow-to-region from to)
          (goto-char (point-min))
          (condition-case nil
              (while (not (eobp))
                (cond
                  ((looking-at "\"") (forward-sexp 1))
                  ((looking-at "#<") (search-forward ">" nil 'move))
                  ((looking-at "\\(\\(\\sw\\|\\s_\\)+\\)")
                   (let* ((sym   (intern-soft (match-string 1)))
                          (type  (cond ((fboundp sym) 'help-function)
                                       ((or (memq sym '(t nil))  (keywordp sym))
                                        nil)
                                       ((and sym  (boundp sym))
                                        'help-variable)
                                       ((and sym  (locate-library (symbol-name sym)))
                                        'help-library))))
                     (when type (help-xref-button 1 type sym)))
                   (goto-char (match-end 1)))
                  (t (forward-char 1))))
            (error nil)))))))

(define-button-type 'help-library
  :supertype 'help-xref
  'help-function #'(lambda (x) (find-library (symbol-name x)))
  'help-echo (purecopy "mouse-2, RET: find this library"))


;; REPLACES ORIGINAL IN `help-mode.el'.
;;
;; Add `C-h S' mention.
;;
(define-button-type 'help-symbol
  :supertype 'help-xref
  'help-function #'describe-symbol
  'help-echo
  (purecopy "mouse-2, RET: describe this symbol, \
C-h S RET: check manuals"))


;; $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
;; REPLACES ORIGINAL IN `help-mode.el'.
;; Provide a tooltip for whatever is under the mouse.
;; This can't be done here - the message needs to be done via an idle timer,
;; whenever mouse is over any name. Perhaps combine with eldoc.
;;
;; ;;;###autoload
;; (defun help-follow-mouse (click)
;;   "Follow the cross-reference that you CLICK on."
;;   (interactive "e")
;;   (let* ((start  (event-start click))
;;       (window (car start))
;;       (pos (car (cdr start))))
;;     (with-current-buffer (window-buffer window)
;;       (message "Display help on `%s'"
;;                (save-excursion
;;                  (goto-char pos) (skip-syntax-backward "w_")
;;                  (buffer-substring (point)
;;                                    (progn (skip-syntax-forward "w_")
;;                                           (point)))))
;;       (help-follow pos))))

;; After a certain idle time, use function `mouse-position', and pick
;; up the symbol under the pointer. Then display a message that
;; clicking mouse-2 will display help on the symbol.


;;;;;;;;;;;;;;;;;;;;;;;;;;;

(provide 'help-mode+)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; help-mode+.el ends here