(require 'info nil t)
(require 'info+ nil t)
(require 'thingatpt nil t)
(when (and (require 'thingatpt+ nil t)
(require 'frame-fns nil t)
(require 'naked nil t)
(require 'help-macro nil t)
(require 'help-macro+ nil t))
(defvar help-origin-buffer nil "Buffer that we left, to go to *Help*.")
(define-key help-map [?\C-m] 'help-on-click/key)
(define-key help-map "u" 'manual-entry)
(define-key help-map "\C-a" 'apropos)
(define-key help-map "\M-o" 'pop-to-help-toggle)
(define-key help-map "\C-l" 'locate-library)
(define-key help-map "\M-a" 'apropos-documentation)
(define-key help-map "\C-\M-a" 'tags-apropos)
(define-key help-map [down-mouse-1] 'mouse-help-on-click)
(define-key help-map [mode-line down-mouse-1] 'mouse-help-on-mode-line-click)
(defun describe-key (&optional key untranslated up-event)
"Describe the command that a keyboard/menu/mouse sequence invokes.
KEY can be any kind of a key sequence
mouse events, and/or menu events. When calling from a program,
pass KEY as a string or a vector.
If non-nil, UNTRANSLATED is a vector of the corresponding untranslated events.
It can also be a number, in which case the untranslated events from
the last key sequence entered are used.
UP-EVENT is the up-event that was discarded by reading KEY, or nil.
If KEY is a menu item or a tool-bar button that is disabled, this command
temporarily enables it to allow getting help on disabled items and buttons.
Return nil if KEY is undefined
(let ((enable-disabled-menus-and-buttons t)
(when (null (cdr yank-menu))
(setq saved-yank-menu (copy-sequence yank-menu))
(menu-bar-update-yank-menu "(any string)" nil))
(setq key (read-key-sequence "Describe key (or click or menu item): "))
(and (vectorp key)
(let ((last-idx (1- (length key))))
(and (eventp (aref key last-idx))
(memq 'down (event-modifiers (aref key last-idx)))))
(or (and (eventp (aref key 0))
(memq 'down (event-modifiers (aref key 0)))
(= (length key) 1))
(and (> (length key) 1)
(eventp (aref key 1))
(memq 'down (event-modifiers (aref key 1)))))
(setq yank-menu (copy-sequence saved-yank-menu))
(fset 'yank-menu (cons 'keymap yank-menu))))))
(when (numberp untranslated) (setq untranslated (this-single-command-raw-keys)))
(let* ((event (aref key (if (and (symbolp (aref key 0))
(> (length key) 1)
(consp (aref key 1)))
(modifiers (event-modifiers event))
(mouse-msg (if (or (memq 'click modifiers)
(memq 'down modifiers)
(memq 'drag modifiers))
" at that spot"
(defn (key-binding key t))
defn-up defn-up-tricky ev-type mouse-1-remapped mouse-1-tricky)
(when (and (eq defn nil)
(stringp (aref key (1- (length key))))
(eq (key-binding (substring key 0 -1)) 'yank-menu))
(setq defn 'menu-bar-select-yank))
(cond ((or (null defn) (integerp defn) (equal defn 'undefined))
(message "%s%s is undefined" (help-key-description key untranslated) mouse-msg)
(help-setup-xref (list #'describe-function defn) (interactive-p))
(when (stringp (aref key (1- (length key))))
(aset key (1- (length key)) "(any string)"))
(when (and untranslated (stringp (aref untranslated (1- (length untranslated)))))
(aset untranslated (1- (length untranslated))
(setq ev-type (event-basic-type up-event))
(let ((sequence (vector up-event)))
(when (and (eq ev-type 'mouse-1)
(not (eq mouse-1-click-follows-link 'double))
(setq mouse-1-remapped (mouse-on-link-p (event-start up-event))))
(setq mouse-1-tricky (and (integerp mouse-1-click-follows-link)
(> mouse-1-click-follows-link 0)))
(cond ((stringp mouse-1-remapped) (setq sequence mouse-1-remapped))
((vectorp mouse-1-remapped) (setcar up-event (elt mouse-1-remapped 0)))
(t (setcar up-event 'mouse-2))))
(setq defn-up (key-binding sequence nil nil (event-start up-event)))
(setq sequence (vector up-event))
(aset sequence 0 'mouse-1)
(setq defn-up-tricky (key-binding sequence nil nil
(princ (help-key-description key untranslated))
(princ (format "\
%s runs the command %S, which is "
(unless (or (null defn-up) (integerp defn-up) (equal defn-up 'undefined))
(princ (format "
----------------- up-event %s----------------
<%S>%s%s runs the command %S, which is "
(if mouse-1-tricky "(short click) " "")
(if mouse-1-remapped " is remapped to <mouse-2>, which" "")
(unless (or (null defn-up-tricky)
(eq defn-up-tricky 'undefined))
(princ (format "
----------------- up-event (long click) ----------------
Pressing <%S>%s for longer than %d milli-seconds
runs the command %S, which is "
"RET [abcCefFhiIkKlLmnopqrsStuvw] C-[\acdeflmoptw] M-[acko] C-M-a (? for more help):"
"This is the Emacs `help-command', accessible via `%THIS-KEY%'.
Type a help option (below) now, for help on a particular topic.
Use \\<help-map>`\\[scroll-up]' or `\\[scroll-down]' to scroll this text. \
Type `\\[help-quit]' to exit Help.
\(A \"command\" is any function that you can execute via `M-x'.)
\\[help-with-tutorial]: Starts a tutorial for learning Emacs.
\\[view-emacs-FAQ]: Explains frequently asked Emacs questions.
\\[help-on-click/key]: Help about a key sequence or something you click with the mouse.
\\[apropos-command]: Shows commands that match a regular expression (regexp).
\\[describe-bindings]: Shows current key bindings: keyboard, menu bar, and mouse.
\\[describe-command]: Shows the doc for an Emacs command.
\\[describe-function]: Shows the doc for an Emacs function.
\\[Info-goto-emacs-command-node]: Opens the Emacs manual for an Emacs command.
\\[info]: Enters `Info', to browse manuals, including Emacs and Emacs Lisp.
\\[describe-key]: Describes the command bound to keyboard/menu/mouse sequence.
\\[Info-goto-emacs-key-command-node]: Opens the Emacs manual for a keyboard/menu/mouse \
\\[describe-mode]: Describes the current major and minor modes.
\\[describe-option]: Shows an Emacs user option's value and documentation.
\\[describe-variable]: Shows an Emacs variable's value and documentation.
MORE ADVANCED HELP
\\[apropos-documentation]: Shows Emacs functions and variables whose doc matches a regexp.
\\[view-lossage]: Shows what you just typed (last 100 keystrokes & mouse actions).
\\[view-emacs-news]: Describes what's new in this Emacs release.
\\[finder-by-keyword]: Finds Emacs-Lisp libraries that match a topic.
\\[describe-syntax]: Describes the current syntax table.
\\[info-lookup-symbol]: Finds a symbol in the manual for the current buffer's language.
\\[manual-entry]: Finds a topic in the Unix manual.
\\[where-is]: Identifies a keyboard/menu/mouse sequence that invokes a command.
\\[apropos]: Shows Emacs functions and variables that match a regexp.
\\[describe-key-briefly]: Identifies the command bound to a keyboard/menu/mouse sequence.
\\[describe-distribution]: Shows Emacs ordering information.
\\[locate-library]: Shows the path name to an Emacs library.
\\[describe-option-of-type]: Shows value and doc for an Emacs user option of a given type.
\\[describe-project]: Shows information about the GNU project.
\\[describe-no-warranty]: Shows information about the absence of a warranty.
\\[describe-copying]: Shows the GNU Emacs General Public License.
\\[pop-to-help-toggle]: Pops to Help buffer or back to the buffer that sent you to Help.
\\[tags-apropos]: Shows the tags matched by a given string.
\\[describe-coding-system]: Describes a coding system.
h Displays the HELLO file, which illustrates scripts and languages.
\\[describe-input-method]: Describes an input method.
\\[describe-language-environment]: Describes a language environment.
(defun where-is (definition &optional insert)
"Show keyboard/menu/mouse sequences that invoke specified command.
Argument is a command definition, usually a symbol with a function
definition. Default candidate is: preferably the
`symbol-nearest-point', or else the innermost function call
surrounding point (`function-called-at-point').
With no prefix argument, only commands actually bound to keys are
completion candidates. With a prefix argument, all commands are
With a plain (non-numeric) prefix argument, `C-u', insert the message
in the current buffer."
(let ((fn (or (and (fboundp 'symbol-nearest-point)
(setq val (completing-read (if fn
(format "Where is command (default %s): " fn)
"Where is command: ")
(and (commandp c)
(where-is-internal c overriding-local-map
(list (if (equal val "") fn (intern val))
(unless definition (error "No command"))
(let ((func (indirect-function definition))
(standard-output (if insert (current-buffer) t)))
(mapatoms (lambda (symbol)
(and (fboundp symbol)
(not (eq symbol definition))
(eq func (condition-case () (indirect-function symbol) (error symbol)))
(push symbol defs))))
(dolist (symbol (cons definition defs))
(let* ((remapped (command-remapping symbol))
(keys (where-is-internal symbol overriding-local-map nil nil remapped))
(keys (mapconcat (if (fboundp 'naked-key-description)
keys "', `"))
(setq string (if insert
(if (> (length keys) 0)
(format "%s (%s) (remapped from %s)" keys remapped symbol)
(format "%s (%s)" keys symbol))
(format "M-x %s RET" symbol))
(if (> (length keys) 0)
(format "%s is remapped to %s which is on `%s'"
symbol remapped keys)
(format "%s is on `%s'" symbol keys))
(and (eq symbol definition)
(format "%s is not on any key" symbol)))))
(unless (eq symbol definition) (princ "))
(defun help-on-click/key-lookup (key &optional pp-key where)
"Look up information on KEY via `describe-key' and `info'.
Optional args PP-KEY and WHERE are strings naming KEY and its type.
Their defaults are KEY's `key-description' and \"Key sequence\".
Function `Info-goto-emacs-key-command-node' is used to look up KEY."
(sit-for 0 200)
(setq where (or where "Key sequence ")
pp-key (or pp-key (if (fboundp 'naked-key-description)
(let* ((described-p (describe-key key))
(documented-p (Info-goto-emacs-key-command-node key)))
(when (and (not documented-p) (get-buffer-window "*info*" 'visible)) (Info-exit))
(cond ((and described-p documented-p)
(when (fboundp 'show-*Help*-buffer) (show-*Help*-buffer))
(message "%s`%s': summary in *Help* buffer
(when (fboundp 'show-*Help*-buffer) (show-*Help*-buffer))
(message "%s`%s': summary in buffer `*Help*'." where pp-key))
(message "%s`%s': doc in buffer `*info*'." where pp-key))
(message "%s`%s' is undefined." where pp-key)))))
(defun help-on-click/key (key)
"Give help on a key/menu sequence or object clicked with the mouse.
The object can be any part of an Emacs window or a name appearing in a
buffer. You can do any of the following:
type a key sequence (e.g. `C-M-s')
choose a menu item (e.g. [menu-bar files open-file])
click on a scroll bar
click on the mode line
click in the minibuffer
click on an Emacs-related name in a buffer: apropos is called
click anywhere else in a buffer: its modes are described
Help is generally provided using `describe-key' and the Emacs online
manual (via `Info-goto-emacs-key-command-node'). If no entry is found
in the index of the Emacs manual, then the manual is searched from the
beginning for literal occurrences of KEY.
For example, the KEY `C-g' is not in the index (for some reason), so
the manual is searched. (Once an occurrence is found, you can
repeatedly type `s' in *Info* to search for additional occurrences.)
If you click on a name in a buffer, then `apropos-documentation' and
`apropos' are used to find information on the name. These functions
are not used when you do something besides click on a name.
If you click elsewhere in a buffer other than the minibuffer, then
`describe-mode' is used to describe the buffer's current mode(s)."
(interactive "kClick mouse on something or type a key sequence.")
(let ((temp-buffer-show-function 'switch-to-buffer-other-window)
(cond ((stringp key)
(let ((type (aref key 0)))
(cond ((or (symbolp type) (integerp type))
(cond ((eq 'mode-line type)
(Info-goto-node "(emacs)Mode Line")
(message "Mode line: decribed in *info* buffer."))
((eq 'menu-bar (car type))
(help-on-click/key-lookup key (aref key (1- (length key))) "Menu item "))
((not (eq 'down (car (event-modifiers (car type)))))
(setq key type)
(posn-window (event-start key)))
(message "Minibuffer: decribed in buffer `*info*'."))
(let ((symb (save-excursion
(if (fboundp 'tap-symbol-at-point)
(message "Looking for info apropos `%s'..." symb)
(when (get-buffer "*Apropos Doc*")
(kill-buffer (get-buffer "*Apropos Doc*")))
(setq symb (format "%s" symb))))))
(set-buffer (get-buffer "*Apropos*"))
(rename-buffer "*Apropos Doc*"))
(when (fboundp '1-window-frames-on)
(let ((frames (1-window-frames-on "*Apropos Doc*")))
(select-frame (car frames))
(rename-frame nil "*Apropos Doc*")
(setq found (apropos symb-regexp))
(setq found (and (consp found)
(or (cdr found) (cadr found))))
(unless found (delete-windows-on "*Apropos*"))
((and found-doc found)
"See buffers `*Apropos*' and `*Apropos Doc*'."))
"See information on `%s' in buffer `*Apropos*'."
"See information on `%s' in buffer `*Apropos Doc*'."
"No information found regarding `%s'."
(let ((bufname (buffer-name (current-buffer))))
(fboundp 'show-*Help*-buffer) (show-*Help*-buffer))
"Mode(s) of buffer `%s' are described in buffer \
(defun mouse-help-on-click (event)
"Give help on an object clicked with the mouse."
(help-on-click/key (vector event)))
(defun mouse-help-on-mode-line-click (event)
"Give help on the mode line."
(help-on-click/key (vector 'mode-line event)))
(defun pop-to-help-toggle ()
"Pop to buffer *Help* or back to the buffer that sent you to *Help*."
(let ((orig-buf (and (buffer-live-p help-origin-buffer)
(if (string-match "*Help*" (buffer-name))
(cond ((not orig-buf) (error "No buffer to return to"))
((string-match "Minibuf" (buffer-name orig-buf))
(window-frame (select-window (minibuffer-window)))))
(t (pop-to-buffer orig-buf)))
(setq help-origin-buffer (current-buffer))