summaryrefslogtreecommitdiff
path: root/lisp/help-fns.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/help-fns.el')
-rw-r--r--lisp/help-fns.el544
1 files changed, 308 insertions, 236 deletions
diff --git a/lisp/help-fns.el b/lisp/help-fns.el
index 52aa0517fa8..958a0754946 100644
--- a/lisp/help-fns.el
+++ b/lisp/help-fns.el
@@ -1,9 +1,9 @@
;;; help-fns.el --- Complex help functions -*- lexical-binding: t -*-
-;; Copyright (C) 1985-1986, 1993-1994, 1998-2013 Free Software
+;; Copyright (C) 1985-1986, 1993-1994, 1998-2015 Free Software
;; Foundation, Inc.
-;; Maintainer: FSF
+;; Maintainer: emacs-devel@gnu.org
;; Keywords: help, internal
;; Package: emacs
@@ -32,8 +32,22 @@
;;; Code:
+(require 'cl-lib)
+(require 'help-mode)
+
+(defvar help-fns-describe-function-functions nil
+ "List of functions to run in help buffer in `describe-function'.
+Those functions will be run after the header line and argument
+list was inserted, and before the documentation will be inserted.
+The functions will receive the function name as argument.")
+
;; Functions
+(defvar describe-function-orig-buffer nil
+ "Buffer that was current when `describe-function' was invoked.
+Functions on `help-fns-describe-function-functions' can use this
+to get buffer-local values.")
+
;;;###autoload
(defun describe-function (function)
"Display the full documentation of FUNCTION (a symbol)."
@@ -48,124 +62,40 @@
(and fn (symbol-name fn))))
(list (if (equal val "")
fn (intern val)))))
- (if (null function)
- (message "You didn't specify a function")
- (help-setup-xref (list #'describe-function function)
- (called-interactively-p 'interactive))
+ (or (and function (symbolp function))
+ (user-error "You didn't specify a function symbol"))
+ (or (fboundp function)
+ (user-error "Symbol's function definition is void: %s" function))
+
+ ;; We save describe-function-orig-buffer on the help xref stack, so
+ ;; it is restored by the back/forward buttons. 'help-buffer'
+ ;; expects (current-buffer) to be a help buffer when processing
+ ;; those buttons, so we can't change the current buffer before
+ ;; calling that.
+ (let ((describe-function-orig-buffer
+ (or describe-function-orig-buffer
+ (current-buffer))))
+
+ (help-setup-xref
+ (list (lambda (function buffer)
+ (let ((describe-function-orig-buffer
+ (if (buffer-live-p buffer) buffer)))
+ (describe-function function)))
+ function describe-function-orig-buffer)
+ (called-interactively-p 'interactive))
+
(save-excursion
(with-help-window (help-buffer)
- (prin1 function)
- ;; Use " is " instead of a colon so that
- ;; it is easier to get out the function name using forward-sexp.
- (princ " is ")
- (describe-function-1 function)
- (with-current-buffer standard-output
- ;; Return the text we displayed.
- (buffer-string))))))
-
-(defun help-split-fundoc (docstring def)
- "Split a function DOCSTRING into the actual doc and the usage info.
-Return (USAGE . DOC) or nil if there's no usage info, where USAGE info
-is a string describing the argument list of DEF, such as
-\"(apply FUNCTION &rest ARGUMENTS)\".
-DEF is the function whose usage we're looking for in DOCSTRING."
- ;; Functions can get the calling sequence at the end of the doc string.
- ;; In cases where `function' has been fset to a subr we can't search for
- ;; function's name in the doc string so we use `fn' as the anonymous
- ;; function name instead.
- (when (and docstring (string-match "\n\n(fn\\(\\( .*\\)?)\\)\\'" docstring))
- (cons (format "(%s%s"
- ;; Replace `fn' with the actual function name.
- (if (symbolp def) def "anonymous")
- (match-string 1 docstring))
- (unless (zerop (match-beginning 0))
- (substring docstring 0 (match-beginning 0))))))
-
-;; FIXME: Move to subr.el?
-(defun help-add-fundoc-usage (docstring arglist)
- "Add the usage info to DOCSTRING.
-If DOCSTRING already has a usage info, then just return it unchanged.
-The usage info is built from ARGLIST. DOCSTRING can be nil.
-ARGLIST can also be t or a string of the form \"(FUN ARG1 ARG2 ...)\"."
- (unless (stringp docstring) (setq docstring ""))
- (if (or (string-match "\n\n(fn\\(\\( .*\\)?)\\)\\'" docstring)
- (eq arglist t))
- docstring
- (concat docstring
- (if (string-match "\n?\n\\'" docstring)
- (if (< (- (match-end 0) (match-beginning 0)) 2) "\n" "")
- "\n\n")
- (if (and (stringp arglist)
- (string-match "\\`([^ ]+\\(.*\\))\\'" arglist))
- (concat "(fn" (match-string 1 arglist) ")")
- (format "%S" (help-make-usage 'fn arglist))))))
-
-;; FIXME: Move to subr.el?
-(defun help-function-arglist (def &optional preserve-names)
- "Return a formal argument list for the function DEF.
-IF PRESERVE-NAMES is non-nil, return a formal arglist that uses
-the same names as used in the original source code, when possible."
- ;; Handle symbols aliased to other symbols.
- (if (and (symbolp def) (fboundp def)) (setq def (indirect-function def)))
- ;; If definition is a macro, find the function inside it.
- (if (eq (car-safe def) 'macro) (setq def (cdr def)))
- (cond
- ((and (byte-code-function-p def) (listp (aref def 0))) (aref def 0))
- ((eq (car-safe def) 'lambda) (nth 1 def))
- ((eq (car-safe def) 'closure) (nth 2 def))
- ((or (and (byte-code-function-p def) (integerp (aref def 0)))
- (subrp def))
- (or (when preserve-names
- (let* ((doc (condition-case nil (documentation def) (error nil)))
- (docargs (if doc (car (help-split-fundoc doc nil))))
- (arglist (if docargs
- (cdar (read-from-string (downcase docargs)))))
- (valid t))
- ;; Check validity.
- (dolist (arg arglist)
- (unless (and (symbolp arg)
- (let ((name (symbol-name arg)))
- (if (eq (aref name 0) ?&)
- (memq arg '(&rest &optional))
- (not (string-match "\\." name)))))
- (setq valid nil)))
- (when valid arglist)))
- (let* ((args-desc (if (not (subrp def))
- (aref def 0)
- (let ((a (subr-arity def)))
- (logior (car a)
- (if (numberp (cdr a))
- (lsh (cdr a) 8)
- (lsh 1 7))))))
- (max (lsh args-desc -8))
- (min (logand args-desc 127))
- (rest (logand args-desc 128))
- (arglist ()))
- (dotimes (i min)
- (push (intern (concat "arg" (number-to-string (1+ i)))) arglist))
- (when (> max min)
- (push '&optional arglist)
- (dotimes (i (- max min))
- (push (intern (concat "arg" (number-to-string (+ 1 i min))))
- arglist)))
- (unless (zerop rest) (push '&rest arglist) (push 'rest arglist))
- (nreverse arglist))))
- ((and (autoloadp def) (not (eq (nth 4 def) 'keymap)))
- "[Arg list not available until function definition is loaded.]")
- (t t)))
-
-;; FIXME: Move to subr.el?
-(defun help-make-usage (function arglist)
- (cons (if (symbolp function) function 'anonymous)
- (mapcar (lambda (arg)
- (if (not (symbolp arg)) arg
- (let ((name (symbol-name arg)))
- (cond
- ((string-match "\\`&" name) arg)
- ((string-match "\\`_" name)
- (intern (upcase (substring name 1))))
- (t (intern (upcase name)))))))
- arglist)))
+ (prin1 function)
+ ;; Use " is " instead of a colon so that
+ ;; it is easier to get out the function name using forward-sexp.
+ (princ " is ")
+ (describe-function-1 function)
+ (with-current-buffer standard-output
+ ;; Return the text we displayed.
+ (buffer-string))))
+ ))
+
;; Could be this, if we make symbol-file do the work below.
;; (defun help-C-file-name (subr-or-var kind)
@@ -181,7 +111,7 @@ KIND should be `var' for a variable or `subr' for a subroutine."
(let ((docbuf (get-buffer-create " *DOC*"))
(name (if (eq 'var kind)
(concat "V" (symbol-name subr-or-var))
- (concat "F" (subr-name subr-or-var)))))
+ (concat "F" (subr-name (advice--cd*r subr-or-var))))))
(with-current-buffer docbuf
(goto-char (point-min))
(if (eobp)
@@ -230,7 +160,7 @@ if the variable `help-downcase-arguments' is non-nil."
"\\)"
"\\(?:es\\|s\\|th\\)?" ; for ARGth, ARGs
"\\(?:-[a-z0-9-]+\\)?" ; for ARG-xxx, ARG-n
- "\\(?:-[{([<`\"].*?\\)?"; for ARG-{x}, (x), <x>, [x], `x'
+ "\\(?:-[{([<`\"‘].*?\\)?"; for ARG-{x}, (x), <x>, [x], `x', ‘x’
"\\>") ; end of word
(help-highlight-arg arg)
doc t t 1)))
@@ -248,7 +178,7 @@ if the variable `help-downcase-arguments' is non-nil."
(skip-chars-forward "^ ")
(while next
(or opt (not (looking-at " &")) (setq opt t))
- (if (not (re-search-forward " \\([\\[(]*\\)\\([^] &)\.]+\\)" nil t))
+ (if (not (re-search-forward " \\([\\[(]*\\)\\([^] &).]+\\)" nil t))
(setq next nil)
(setq args (cons (match-string 2) args))
(when (and opt (string= (match-string 1) "("))
@@ -280,8 +210,7 @@ OBJECT should be a symbol associated with a function, variable, or face;
alternatively, it can be a function definition.
If TYPE is `defvar', search for a variable definition.
If TYPE is `defface', search for a face definition.
-If TYPE is the value returned by `symbol-function' for a function symbol,
- search for a function definition.
+If TYPE is not a symbol, search for a function definition.
The return value is the absolute name of a readable file where OBJECT is
defined. If several such files exist, preference is given to a file
@@ -291,9 +220,10 @@ suitable file is found, return nil."
(let* ((autoloaded (autoloadp type))
(file-name (or (and autoloaded (nth 1 type))
(symbol-file
- object (if (memq type (list 'defvar 'defface))
- type
- 'defun)))))
+ ;; FIXME: Why do we have this weird "If TYPE is the
+ ;; value returned by `symbol-function' for a function
+ ;; symbol" exception?
+ object (or (if (symbolp type) type) 'defun)))))
(cond
(autoloaded
;; An autoloaded function: Locate the file since `symbol-function'
@@ -367,23 +297,9 @@ suitable file is found, return nil."
lib-name)
file-name))
;; The next three forms are from `find-source-lisp-file'.
- (elc-file (locate-file
- (concat file-name
- (if (string-match "\\.el\\'" file-name)
- "c"
- ".elc"))
- load-path nil 'readable))
- (str (when elc-file
- (with-temp-buffer
- (insert-file-contents-literally elc-file nil 0 256)
- (buffer-string))))
- (src-file (and str
- (string-match ";;; from file \\(.*\\.el\\)" str)
- (match-string 1 str))))
+ (src-file (locate-library file-name t nil 'readable)))
(and src-file (file-readable-p src-file) src-file))))))
-(declare-function ad-get-advice-info "advice" (function))
-
(defun help-fns--key-bindings (function)
(when (commandp function)
(let ((pt2 (with-current-buffer standard-output (point)))
@@ -403,7 +319,7 @@ suitable file is found, return nil."
(when remapped
(princ "Its keys are remapped to ")
(princ (if (symbolp remapped)
- (concat "`" (symbol-name remapped) "'")
+ (format-message "`%s'" remapped)
"an anonymous command"))
(princ ".\n"))
@@ -428,7 +344,7 @@ suitable file is found, return nil."
(with-current-buffer standard-output
(fill-region-as-paragraph pt2 (point))
- (unless (looking-back "\n\n")
+ (unless (looking-back "\n\n" (- (point) 2))
(terpri))))))
(defun help-fns--compiler-macro (function)
@@ -437,21 +353,25 @@ suitable file is found, return nil."
(insert "\nThis function has a compiler macro")
(if (symbolp handler)
(progn
- (insert (format " `%s'" handler))
+ (insert (format-message " `%s'" handler))
(save-excursion
- (re-search-backward "`\\([^`']+\\)'" nil t)
+ (re-search-backward (substitute-command-keys "`\\([^`']+\\)'")
+ nil t)
(help-xref-button 1 'help-function handler)))
;; FIXME: Obsolete since 24.4.
(let ((lib (get function 'compiler-macro-file)))
(when (stringp lib)
- (insert (format " in `%s'" lib))
+ (insert (format-message " in `%s'" lib))
(save-excursion
- (re-search-backward "`\\([^`']+\\)'" nil t)
+ (re-search-backward (substitute-command-keys "`\\([^`']+\\)'")
+ nil t)
(help-xref-button 1 'help-function-cmacro function lib)))))
(insert ".\n"))))
-(defun help-fns--signature (function doc real-def real-function)
- (unless (keymapp function) ; If definition is a keymap, skip arglist note.
+(defun help-fns--signature (function doc real-def real-function buffer)
+ "Insert usage at point and return docstring. With highlighting."
+ (if (keymapp function)
+ doc ; If definition is a keymap, skip arglist note.
(let* ((advertised (gethash real-def advertised-signature-table t))
(arglist (if (listp advertised)
advertised (help-function-arglist real-def)))
@@ -460,7 +380,7 @@ suitable file is found, return nil."
(let* ((use (cond
((and usage (not (listp advertised))) (car usage))
((listp arglist)
- (format "%S" (help-make-usage function arglist)))
+ (help--make-usage-docstring function arglist))
((stringp arglist) arglist)
;; Maybe the arglist is in the docstring of a symbol
;; this one is aliased to.
@@ -474,13 +394,27 @@ suitable file is found, return nil."
(car usage))
((or (stringp real-def)
(vectorp real-def))
- (format "\nMacro: %s" (format-kbd-macro real-def)))
+ (format "\nMacro: %s"
+ (help--docstring-quote
+ (format-kbd-macro real-def))))
(t "[Missing arglist. Please make a bug report.]")))
- (high (help-highlight-arguments use doc)))
- (let ((fill-begin (point)))
- (insert (car high) "\n")
- (fill-region fill-begin (point)))
- (cdr high)))))
+ ;; Insert "`X", not "(\` X)", when documenting `X.
+ (use1 (replace-regexp-in-string
+ "\\`(\\\\=\\\\\\\\=` \\([^\n ]*\\))\\'"
+ "\\\\=`\\1" use t))
+ (high (if buffer
+ (let (subst-use1 subst-doc)
+ (with-current-buffer buffer
+ (setq subst-use1 (substitute-command-keys use1))
+ (setq subst-doc (substitute-command-keys doc)))
+ (help-highlight-arguments subst-use1 subst-doc))
+ (cons use1 doc))))
+ (let ((fill-begin (point))
+ (high-usage (car high))
+ (high-doc (cdr high)))
+ (insert high-usage "\n")
+ (fill-region fill-begin (point))
+ high-doc)))))
(defun help-fns--parent-mode (function)
;; If this is a derived mode, link to the parent.
@@ -488,13 +422,13 @@ suitable file is found, return nil."
(get function
'derived-mode-parent))))
(when parent-mode
- (insert "\nParent mode: `")
+ (insert (substitute-command-keys "\nParent mode: `"))
(let ((beg (point)))
(insert (format "%s" parent-mode))
(make-text-button beg (point)
'type 'help-function
'help-args (list parent-mode)))
- (insert "'.\n"))))
+ (insert (substitute-command-keys "'.\n")))))
(defun help-fns--obsolete (function)
;; Ignore lambda constructs, keyboard macros, etc.
@@ -510,7 +444,7 @@ suitable file is found, return nil."
(when (nth 2 obsolete)
(insert (format " since %s" (nth 2 obsolete))))
(insert (cond ((stringp use) (concat ";\n" use))
- (use (format ";\nuse `%s' instead." use))
+ (use (format-message ";\nuse `%s' instead." use))
(t "."))
"\n"))))
@@ -529,35 +463,76 @@ FILE is the file where FUNCTION was probably defined."
(setq load-hist (cdr load-hist)))
found))
+(defun help-fns--interactive-only (function)
+ "Insert some help blurb if FUNCTION should only be used interactively."
+ ;; Ignore lambda constructs, keyboard macros, etc.
+ (and (symbolp function)
+ (not (eq (car-safe (symbol-function function)) 'macro))
+ (let* ((interactive-only
+ (or (get function 'interactive-only)
+ (if (boundp 'byte-compile-interactive-only-functions)
+ (memq function
+ byte-compile-interactive-only-functions)))))
+ (when interactive-only
+ (insert "\nThis function is for interactive use only"
+ ;; Cf byte-compile-form.
+ (cond ((stringp interactive-only)
+ (format ";\nin Lisp code %s" interactive-only))
+ ((and (symbolp 'interactive-only)
+ (not (eq interactive-only t)))
+ (format-message ";\nin Lisp code use `%s' instead."
+ interactive-only))
+ (t "."))
+ "\n")))))
+
+(defun help-fns-short-filename (filename)
+ (let* ((abbrev (abbreviate-file-name filename))
+ (short abbrev))
+ (dolist (dir load-path)
+ (let ((rel (file-relative-name filename dir)))
+ (if (< (length rel) (length short))
+ (setq short rel)))
+ (let ((rel (file-relative-name abbrev dir)))
+ (if (< (length rel) (length short))
+ (setq short rel))))
+ short))
+
;;;###autoload
(defun describe-function-1 (function)
- (let* ((advised (and (symbolp function) (featurep 'advice)
- (ad-get-advice-info function)))
+ (let* ((advised (and (symbolp function)
+ (featurep 'nadvice)
+ (advice--p (advice--symbol-function function))))
;; If the function is advised, use the symbol that has the
;; real definition, if that symbol is already set up.
(real-function
(or (and advised
- (let ((origname (cdr (assq 'origname advised))))
- (and (fboundp origname) origname)))
+ (advice--cd*r (advice--symbol-function function)))
function))
;; Get the real definition.
(def (if (symbolp real-function)
- (symbol-function real-function)
- function))
- (aliased (symbolp def))
- (real-def (if aliased
- (let ((f def))
- (while (and (fboundp f)
- (symbolp (symbol-function f)))
- (setq f (symbol-function f)))
- f)
- def))
+ (or (symbol-function real-function)
+ (signal 'void-function (list real-function)))
+ real-function))
+ (aliased (or (symbolp def)
+ ;; Advised & aliased function.
+ (and advised (symbolp real-function))))
+ (real-def (cond
+ (aliased (let ((f real-function))
+ (while (and (fboundp f)
+ (symbolp (symbol-function f)))
+ (setq f (symbol-function f)))
+ f))
+ ((subrp def) (intern (subr-name def)))
+ (t def)))
+ (sig-key (if (subrp def)
+ (indirect-function real-def)
+ real-def))
(file-name (find-lisp-object-file-name function def))
(pt1 (with-current-buffer (help-buffer) (point)))
(beg (if (and (or (byte-code-function-p def)
(keymapp def)
(memq (car-safe def) '(macro lambda closure)))
- file-name
+ (stringp file-name)
(help-fns--autoloaded-p function file-name))
(if (commandp def)
"an interactive autoloaded "
@@ -571,21 +546,27 @@ FILE is the file where FUNCTION was probably defined."
(if (eq 'unevalled (cdr (subr-arity def)))
(concat beg "special form")
(concat beg "built-in function")))
- ((byte-code-function-p def)
- (concat beg "compiled Lisp function"))
+ ;; Aliases are Lisp functions, so we need to check
+ ;; aliases before functions.
(aliased
- (format "an alias for `%s'" real-def))
- ((eq (car-safe def) 'lambda)
- (concat beg "Lisp function"))
- ((eq (car-safe def) 'macro)
- (concat beg "Lisp macro"))
- ((eq (car-safe def) 'closure)
- (concat beg "Lisp closure"))
+ (format-message "an alias for `%s'" real-def))
((autoloadp def)
(format "%s autoloaded %s"
(if (commandp def) "an interactive" "an")
(if (eq (nth 4 def) 'keymap) "keymap"
(if (nth 4 def) "Lisp macro" "Lisp function"))))
+ ((or (eq (car-safe def) 'macro)
+ ;; For advised macros, def is a lambda
+ ;; expression or a byte-code-function-p, so we
+ ;; need to check macros before functions.
+ (macrop function))
+ (concat beg "Lisp macro"))
+ ((byte-code-function-p def)
+ (concat beg "compiled Lisp function"))
+ ((eq (car-safe def) 'lambda)
+ (concat beg "Lisp function"))
+ ((eq (car-safe def) 'closure)
+ (concat beg "Lisp closure"))
((keymapp def)
(let ((is-full nil)
(elts (cdr-safe def)))
@@ -602,21 +583,23 @@ FILE is the file where FUNCTION was probably defined."
(with-current-buffer standard-output
(save-excursion
(save-match-data
- (when (re-search-backward "alias for `\\([^`']+\\)'" nil t)
+ (when (re-search-backward (substitute-command-keys
+ "alias for `\\([^`']+\\)'")
+ nil t)
(help-xref-button 1 'help-function real-def)))))
(when file-name
- (princ " in `")
;; We used to add .el to the file name,
;; but that's completely wrong when the user used load-file.
- (princ (if (eq file-name 'C-source)
- "C source code"
- (file-name-nondirectory file-name)))
- (princ "'")
+ (princ (format-message " in `%s'"
+ (if (eq file-name 'C-source)
+ "C source code"
+ (help-fns-short-filename file-name))))
;; Make a hyperlink to the library.
(with-current-buffer standard-output
(save-excursion
- (re-search-backward "`\\([^`']+\\)'" nil t)
+ (re-search-backward (substitute-command-keys "`\\([^`']+\\)'")
+ nil t)
(help-xref-button 1 'help-function-def function file-name))))
(princ ".")
(with-current-buffer (help-buffer)
@@ -624,27 +607,34 @@ FILE is the file where FUNCTION was probably defined."
(point)))
(terpri)(terpri)
- (let* ((doc-raw (documentation function t))
- ;; If the function is autoloaded, and its docstring has
- ;; key substitution constructs, load the library.
- (doc (progn
- (and (autoloadp real-def) doc-raw
- help-enable-auto-load
- (string-match "\\([^\\]=\\|[^=]\\|\\`\\)\\\\[[{<]"
- doc-raw)
- (load (cadr real-def) t))
- (substitute-command-keys doc-raw))))
+ (let ((doc-raw (documentation function t))
+ (key-bindings-buffer (current-buffer)))
+
+ ;; If the function is autoloaded, and its docstring has
+ ;; key substitution constructs, load the library.
+ (and (autoloadp real-def) doc-raw
+ help-enable-auto-load
+ (string-match "\\([^\\]=\\|[^=]\\|\\`\\)\\\\[[{<]" doc-raw)
+ (autoload-do-load real-def))
(help-fns--key-bindings function)
(with-current-buffer standard-output
- (setq doc (help-fns--signature function doc real-def real-function))
-
- (help-fns--compiler-macro function)
- (help-fns--parent-mode function)
- (help-fns--obsolete function)
-
- (insert "\n"
- (or doc "Not documented.")))))))
+ (let ((doc (help-fns--signature function doc-raw sig-key
+ real-function key-bindings-buffer)))
+ (run-hook-with-args 'help-fns-describe-function-functions function)
+ (insert "\n"
+ (or doc "Not documented."))
+ ;; Avoid asking the user annoying questions if she decides
+ ;; to save the help buffer, when her locale's codeset
+ ;; isn't UTF-8.
+ (unless (memq text-quoting-style '(straight grave))
+ (set-buffer-file-coding-system 'utf-8))))))))
+
+;; Add defaults to `help-fns-describe-function-functions'.
+(add-hook 'help-fns-describe-function-functions #'help-fns--obsolete)
+(add-hook 'help-fns-describe-function-functions #'help-fns--interactive-only)
+(add-hook 'help-fns-describe-function-functions #'help-fns--parent-mode)
+(add-hook 'help-fns-describe-function-functions #'help-fns--compiler-macro)
;; Variables
@@ -748,14 +738,16 @@ it is displayed along with the global value."
(if file-name
(progn
- (princ " is a variable defined in `")
- (princ (if (eq file-name 'C-source)
- "C source code"
- (file-name-nondirectory file-name)))
- (princ "'.\n")
+ (princ (format-message
+ " is a variable defined in `%s'.\n"
+ (if (eq file-name 'C-source)
+ "C source code"
+ (file-name-nondirectory file-name))))
(with-current-buffer standard-output
(save-excursion
- (re-search-backward "`\\([^`']+\\)'" nil t)
+ (re-search-backward (substitute-command-keys
+ "`\\([^`']+\\)'")
+ nil t)
(help-xref-button 1 'help-variable-def
variable file-name)))
(if valvoid
@@ -763,7 +755,7 @@ it is displayed along with the global value."
(princ "Its ")))
(if valvoid
(princ " is void as a variable.")
- (princ "'s "))))
+ (princ (substitute-command-keys "'s ")))))
(unless valvoid
(with-current-buffer standard-output
(setq val-start-pos (point))
@@ -876,16 +868,21 @@ it is displayed along with the global value."
((not permanent-local))
((bufferp locus)
(setq extra-line t)
- (princ " This variable's buffer-local value is permanent.\n"))
+ (princ
+ (substitute-command-keys
+ " This variable's buffer-local value is permanent.\n")))
(t
(setq extra-line t)
- (princ " This variable's value is permanent \
-if it is given a local binding.\n")))
+ (princ (substitute-command-keys
+ " This variable's value is permanent \
+if it is given a local binding.\n"))))
;; Mention if it's an alias.
(unless (eq alias variable)
(setq extra-line t)
- (princ (format " This variable is an alias for `%s'.\n" alias)))
+ (princ (format-message
+ " This variable is an alias for `%s'.\n"
+ alias)))
(when obsolete
(setq extra-line t)
@@ -893,19 +890,26 @@ if it is given a local binding.\n")))
(if (nth 2 obsolete)
(princ (format " since %s" (nth 2 obsolete))))
(princ (cond ((stringp use) (concat ";\n " use))
- (use (format ";\n use `%s' instead." (car obsolete)))
+ (use (format-message ";\n use `%s' instead."
+ (car obsolete)))
(t ".")))
(terpri))
- (when (member (cons variable val) file-local-variables-alist)
+ (when (member (cons variable val)
+ (with-current-buffer buffer
+ file-local-variables-alist))
(setq extra-line t)
- (if (member (cons variable val) dir-local-variables-alist)
- (let ((file (and (buffer-file-name)
- (not (file-remote-p (buffer-file-name)))
+ (if (member (cons variable val)
+ (with-current-buffer buffer
+ dir-local-variables-alist))
+ (let ((file (and (buffer-file-name buffer)
+ (not (file-remote-p
+ (buffer-file-name buffer)))
(dir-locals-find-file
- (buffer-file-name))))
+ (buffer-file-name buffer))))
(dir-file t))
- (princ " This variable's value is directory-local")
+ (princ (substitute-command-keys
+ " This variable's value is directory-local"))
(if (null file)
(princ ".\n")
(princ ", set ")
@@ -916,16 +920,19 @@ if it is given a local binding.\n")))
(setq file (expand-file-name
dir-locals-file (car file)))
;; Otherwise, assume it was set directly.
- (setq dir-file nil)))
- (princ (if dir-file
- "by the file\n `"
- "for the directory\n `"))
+ (setq file (car file)
+ dir-file nil)))
+ (princ (substitute-command-keys
+ (if dir-file
+ "by the file\n `"
+ "for the directory\n `")))
(with-current-buffer standard-output
(insert-text-button
file 'type 'help-dir-local-var-def
'help-args (list variable file)))
- (princ "'.\n")))
- (princ " This variable's value is file-local.\n")))
+ (princ (substitute-command-keys "'.\n"))))
+ (princ (substitute-command-keys
+ " This variable's value is file-local.\n"))))
(when (memq variable ignored-local-variables)
(setq extra-line t)
@@ -938,8 +945,9 @@ variable.\n"))
(princ " This variable may be risky if used as a \
file-local variable.\n")
(when (assq variable safe-local-variable-values)
- (princ " However, you have added it to \
-`safe-local-variable-values'.\n")))
+ (princ (substitute-command-keys
+ " However, you have added it to \
+`safe-local-variable-values'.\n"))))
(when safe-var
(setq extra-line t)
@@ -947,7 +955,7 @@ file-local variable.\n")
(princ "if its value\n satisfies the predicate ")
(princ (if (byte-code-function-p safe-var)
"which is a byte-compiled expression.\n"
- (format "`%s'.\n" safe-var))))
+ (format-message "`%s'.\n" safe-var))))
(if extra-line (terpri))
(princ "Documentation:\n")
@@ -965,7 +973,7 @@ file-local variable.\n")
(re-search-backward
(concat "\\(" customize-label "\\)") nil t)
(help-xref-button 1 'help-customize-variable variable))))
- ;; Note variable's version or package version
+ ;; Note variable's version or package version.
(let ((output (describe-variable-custom-version-info variable)))
(when output
(terpri)
@@ -977,6 +985,70 @@ file-local variable.\n")
(buffer-string))))))))
+(defvar help-xref-stack-item)
+
+;;;###autoload
+(defun describe-symbol (symbol &optional buffer frame)
+ "Display the full documentation of SYMBOL.
+Will show the info of SYMBOL as a function, variable, and/or face."
+ (interactive
+ (let* ((v-or-f (symbol-at-point))
+ (found (cl-some (lambda (x) (funcall (nth 1 x) v-or-f))
+ describe-symbol-backends))
+ (v-or-f (if found v-or-f (function-called-at-point)))
+ (found (or found v-or-f))
+ (enable-recursive-minibuffers t)
+ (val (completing-read (if found
+ (format
+ "Describe symbol (default %s): " v-or-f)
+ "Describe symbol: ")
+ obarray
+ (lambda (vv)
+ (cl-some (lambda (x) (funcall (nth 1 x) vv))
+ describe-symbol-backends))
+ t nil nil
+ (if found (symbol-name v-or-f)))))
+ (list (if (equal val "")
+ v-or-f (intern val)))))
+ (if (not (symbolp symbol))
+ (user-error "You didn't specify a function or variable"))
+ (unless (buffer-live-p buffer) (setq buffer (current-buffer)))
+ (unless (frame-live-p frame) (setq frame (selected-frame)))
+ (with-current-buffer (help-buffer)
+ ;; Push the previous item on the stack before clobbering the output buffer.
+ (help-setup-xref nil nil)
+ (let* ((docs
+ (nreverse
+ (delq nil
+ (mapcar (pcase-lambda (`(,name ,testfn ,descfn))
+ (when (funcall testfn symbol)
+ ;; Don't record the current entry in the stack.
+ (setq help-xref-stack-item nil)
+ (cons name
+ (funcall descfn symbol buffer frame))))
+ describe-symbol-backends))))
+ (single (null (cdr docs))))
+ (while (cdr docs)
+ (goto-char (point-min))
+ (let ((inhibit-read-only t)
+ (name (caar docs)) ;Name of doc currently at BOB.
+ (doc (cdr (cadr docs)))) ;Doc to add at BOB.
+ (insert doc)
+ (delete-region (point) (progn (skip-chars-backward " \t\n") (point)))
+ (insert "\n\n"
+ (eval-when-compile
+ (propertize "\n" 'face '(:height 0.1 :inverse-video t)))
+ "\n")
+ (when name
+ (insert (symbol-name symbol)
+ " is also a " name "." "\n\n")))
+ (setq docs (cdr docs)))
+ (unless single
+ ;; Don't record the `describe-variable' item in the stack.
+ (setq help-xref-stack-item nil)
+ (help-setup-xref (list #'describe-symbol symbol) nil))
+ (goto-char (point-min)))))
+
;;;###autoload
(defun describe-syntax (&optional buffer)
"Describe the syntax specifications in the syntax table of BUFFER.