| 1 | ;;; help-fns.el --- Complex help functions -*- lexical-binding: t -*- |
| 2 | |
| 3 | ;; Copyright (C) 1985-1986, 1993-1994, 1998-2014 Free Software |
| 4 | ;; Foundation, Inc. |
| 5 | |
| 6 | ;; Maintainer: FSF |
| 7 | ;; Keywords: help, internal |
| 8 | ;; Package: emacs |
| 9 | |
| 10 | ;; This file is part of GNU Emacs. |
| 11 | |
| 12 | ;; GNU Emacs is free software: you can redistribute it and/or modify |
| 13 | ;; it under the terms of the GNU General Public License as published by |
| 14 | ;; the Free Software Foundation, either version 3 of the License, or |
| 15 | ;; (at your option) any later version. |
| 16 | |
| 17 | ;; GNU Emacs is distributed in the hope that it will be useful, |
| 18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 20 | ;; GNU General Public License for more details. |
| 21 | |
| 22 | ;; You should have received a copy of the GNU General Public License |
| 23 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
| 24 | |
| 25 | ;;; Commentary: |
| 26 | |
| 27 | ;; This file contains those help commands which are complicated, and |
| 28 | ;; which may not be used in every session. For example |
| 29 | ;; `describe-function' will probably be heavily used when doing elisp |
| 30 | ;; programming, but not if just editing C files. Simpler help commands |
| 31 | ;; are in help.el |
| 32 | |
| 33 | ;;; Code: |
| 34 | |
| 35 | (defvar help-fns-describe-function-functions nil |
| 36 | "List of functions to run in help buffer in `describe-function'. |
| 37 | Those functions will be run after the header line and argument |
| 38 | list was inserted, and before the documentation will be inserted. |
| 39 | The functions will receive the function name as argument.") |
| 40 | |
| 41 | ;; Functions |
| 42 | |
| 43 | ;;;###autoload |
| 44 | (defun describe-function (function) |
| 45 | "Display the full documentation of FUNCTION (a symbol)." |
| 46 | (interactive |
| 47 | (let ((fn (function-called-at-point)) |
| 48 | (enable-recursive-minibuffers t) |
| 49 | val) |
| 50 | (setq val (completing-read (if fn |
| 51 | (format "Describe function (default %s): " fn) |
| 52 | "Describe function: ") |
| 53 | obarray 'fboundp t nil nil |
| 54 | (and fn (symbol-name fn)))) |
| 55 | (list (if (equal val "") |
| 56 | fn (intern val))))) |
| 57 | (if (null function) |
| 58 | (message "You didn't specify a function") |
| 59 | (help-setup-xref (list #'describe-function function) |
| 60 | (called-interactively-p 'interactive)) |
| 61 | (save-excursion |
| 62 | (with-help-window (help-buffer) |
| 63 | (prin1 function) |
| 64 | ;; Use " is " instead of a colon so that |
| 65 | ;; it is easier to get out the function name using forward-sexp. |
| 66 | (princ " is ") |
| 67 | (describe-function-1 function) |
| 68 | (with-current-buffer standard-output |
| 69 | ;; Return the text we displayed. |
| 70 | (buffer-string)))))) |
| 71 | |
| 72 | (defun help-split-fundoc (docstring def) |
| 73 | "Split a function DOCSTRING into the actual doc and the usage info. |
| 74 | Return (USAGE . DOC) or nil if there's no usage info, where USAGE info |
| 75 | is a string describing the argument list of DEF, such as |
| 76 | \"(apply FUNCTION &rest ARGUMENTS)\". |
| 77 | DEF is the function whose usage we're looking for in DOCSTRING." |
| 78 | ;; Functions can get the calling sequence at the end of the doc string. |
| 79 | ;; In cases where `function' has been fset to a subr we can't search for |
| 80 | ;; function's name in the doc string so we use `fn' as the anonymous |
| 81 | ;; function name instead. |
| 82 | (when (and docstring (string-match "\n\n(fn\\(\\( .*\\)?)\\)\\'" docstring)) |
| 83 | (cons (format "(%s%s" |
| 84 | ;; Replace `fn' with the actual function name. |
| 85 | (if (symbolp def) def "anonymous") |
| 86 | (match-string 1 docstring)) |
| 87 | (unless (zerop (match-beginning 0)) |
| 88 | (substring docstring 0 (match-beginning 0)))))) |
| 89 | |
| 90 | ;; FIXME: Move to subr.el? |
| 91 | (defun help-add-fundoc-usage (docstring arglist) |
| 92 | "Add the usage info to DOCSTRING. |
| 93 | If DOCSTRING already has a usage info, then just return it unchanged. |
| 94 | The usage info is built from ARGLIST. DOCSTRING can be nil. |
| 95 | ARGLIST can also be t or a string of the form \"(FUN ARG1 ARG2 ...)\"." |
| 96 | (unless (stringp docstring) (setq docstring "")) |
| 97 | (if (or (string-match "\n\n(fn\\(\\( .*\\)?)\\)\\'" docstring) |
| 98 | (eq arglist t)) |
| 99 | docstring |
| 100 | (concat docstring |
| 101 | (if (string-match "\n?\n\\'" docstring) |
| 102 | (if (< (- (match-end 0) (match-beginning 0)) 2) "\n" "") |
| 103 | "\n\n") |
| 104 | (if (and (stringp arglist) |
| 105 | (string-match "\\`([^ ]+\\(.*\\))\\'" arglist)) |
| 106 | (concat "(fn" (match-string 1 arglist) ")") |
| 107 | (format "%S" (help-make-usage 'fn arglist)))))) |
| 108 | |
| 109 | ;; FIXME: Move to subr.el? |
| 110 | (defun help-function-arglist (def &optional preserve-names) |
| 111 | "Return a formal argument list for the function DEF. |
| 112 | IF PRESERVE-NAMES is non-nil, return a formal arglist that uses |
| 113 | the same names as used in the original source code, when possible." |
| 114 | ;; Handle symbols aliased to other symbols. |
| 115 | (if (and (symbolp def) (fboundp def)) (setq def (indirect-function def))) |
| 116 | ;; If definition is a macro, find the function inside it. |
| 117 | (if (eq (car-safe def) 'macro) (setq def (cdr def))) |
| 118 | (cond |
| 119 | ((and (byte-code-function-p def) (listp (aref def 0))) (aref def 0)) |
| 120 | ((eq (car-safe def) 'lambda) (nth 1 def)) |
| 121 | ((eq (car-safe def) 'closure) (nth 2 def)) |
| 122 | ((or (and (byte-code-function-p def) (integerp (aref def 0))) |
| 123 | (subrp def)) |
| 124 | (or (when preserve-names |
| 125 | (let* ((doc (condition-case nil (documentation def) (error nil))) |
| 126 | (docargs (if doc (car (help-split-fundoc doc nil)))) |
| 127 | (arglist (if docargs |
| 128 | (cdar (read-from-string (downcase docargs))))) |
| 129 | (valid t)) |
| 130 | ;; Check validity. |
| 131 | (dolist (arg arglist) |
| 132 | (unless (and (symbolp arg) |
| 133 | (let ((name (symbol-name arg))) |
| 134 | (if (eq (aref name 0) ?&) |
| 135 | (memq arg '(&rest &optional)) |
| 136 | (not (string-match "\\." name))))) |
| 137 | (setq valid nil))) |
| 138 | (when valid arglist))) |
| 139 | (let* ((args-desc (if (not (subrp def)) |
| 140 | (aref def 0) |
| 141 | (let ((a (subr-arity def))) |
| 142 | (logior (car a) |
| 143 | (if (numberp (cdr a)) |
| 144 | (lsh (cdr a) 8) |
| 145 | (lsh 1 7)))))) |
| 146 | (max (lsh args-desc -8)) |
| 147 | (min (logand args-desc 127)) |
| 148 | (rest (logand args-desc 128)) |
| 149 | (arglist ())) |
| 150 | (dotimes (i min) |
| 151 | (push (intern (concat "arg" (number-to-string (1+ i)))) arglist)) |
| 152 | (when (> max min) |
| 153 | (push '&optional arglist) |
| 154 | (dotimes (i (- max min)) |
| 155 | (push (intern (concat "arg" (number-to-string (+ 1 i min)))) |
| 156 | arglist))) |
| 157 | (unless (zerop rest) (push '&rest arglist) (push 'rest arglist)) |
| 158 | (nreverse arglist)))) |
| 159 | ((and (autoloadp def) (not (eq (nth 4 def) 'keymap))) |
| 160 | "[Arg list not available until function definition is loaded.]") |
| 161 | (t t))) |
| 162 | |
| 163 | ;; FIXME: Move to subr.el? |
| 164 | (defun help-make-usage (function arglist) |
| 165 | (cons (if (symbolp function) function 'anonymous) |
| 166 | (mapcar (lambda (arg) |
| 167 | (if (not (symbolp arg)) arg |
| 168 | (let ((name (symbol-name arg))) |
| 169 | (cond |
| 170 | ((string-match "\\`&" name) arg) |
| 171 | ((string-match "\\`_" name) |
| 172 | (intern (upcase (substring name 1)))) |
| 173 | (t (intern (upcase name))))))) |
| 174 | arglist))) |
| 175 | |
| 176 | ;; Could be this, if we make symbol-file do the work below. |
| 177 | ;; (defun help-C-file-name (subr-or-var kind) |
| 178 | ;; "Return the name of the C file where SUBR-OR-VAR is defined. |
| 179 | ;; KIND should be `var' for a variable or `subr' for a subroutine." |
| 180 | ;; (symbol-file (if (symbolp subr-or-var) subr-or-var |
| 181 | ;; (subr-name subr-or-var)) |
| 182 | ;; (if (eq kind 'var) 'defvar 'defun))) |
| 183 | ;;;###autoload |
| 184 | (defun help-C-file-name (subr-or-var kind) |
| 185 | "Return the name of the C file where SUBR-OR-VAR is defined. |
| 186 | KIND should be `var' for a variable or `subr' for a subroutine." |
| 187 | (let ((docbuf (get-buffer-create " *DOC*")) |
| 188 | (name (if (eq 'var kind) |
| 189 | (concat "V" (symbol-name subr-or-var)) |
| 190 | (concat "F" (subr-name (advice--cd*r subr-or-var)))))) |
| 191 | (with-current-buffer docbuf |
| 192 | (goto-char (point-min)) |
| 193 | (if (eobp) |
| 194 | (insert-file-contents-literally |
| 195 | (expand-file-name internal-doc-file-name doc-directory))) |
| 196 | (let ((file (catch 'loop |
| 197 | (while t |
| 198 | (let ((pnt (search-forward (concat "\1f" name "\n")))) |
| 199 | (re-search-backward "\1fS\\(.*\\)") |
| 200 | (let ((file (match-string 1))) |
| 201 | (if (member file build-files) |
| 202 | (throw 'loop file) |
| 203 | (goto-char pnt)))))))) |
| 204 | (if (string-match "^ns.*\\(\\.o\\|obj\\)\\'" file) |
| 205 | (setq file (replace-match ".m" t t file 1)) |
| 206 | (if (string-match "\\.\\(o\\|obj\\)\\'" file) |
| 207 | (setq file (replace-match ".c" t t file)))) |
| 208 | (if (string-match "\\.\\(c\\|m\\)\\'" file) |
| 209 | (concat "src/" file) |
| 210 | file))))) |
| 211 | |
| 212 | (defcustom help-downcase-arguments nil |
| 213 | "If non-nil, argument names in *Help* buffers are downcased." |
| 214 | :type 'boolean |
| 215 | :group 'help |
| 216 | :version "23.2") |
| 217 | |
| 218 | (defun help-highlight-arg (arg) |
| 219 | "Highlight ARG as an argument name for a *Help* buffer. |
| 220 | Return ARG in face `help-argument-name'; ARG is also downcased |
| 221 | if the variable `help-downcase-arguments' is non-nil." |
| 222 | (propertize (if help-downcase-arguments (downcase arg) arg) |
| 223 | 'face 'help-argument-name)) |
| 224 | |
| 225 | (defun help-do-arg-highlight (doc args) |
| 226 | (with-syntax-table (make-syntax-table emacs-lisp-mode-syntax-table) |
| 227 | (modify-syntax-entry ?\- "w") |
| 228 | (dolist (arg args) |
| 229 | (setq doc (replace-regexp-in-string |
| 230 | ;; This is heuristic, but covers all common cases |
| 231 | ;; except ARG1-ARG2 |
| 232 | (concat "\\<" ; beginning of word |
| 233 | "\\(?:[a-z-]*-\\)?" ; for xxx-ARG |
| 234 | "\\(" |
| 235 | (regexp-quote arg) |
| 236 | "\\)" |
| 237 | "\\(?:es\\|s\\|th\\)?" ; for ARGth, ARGs |
| 238 | "\\(?:-[a-z0-9-]+\\)?" ; for ARG-xxx, ARG-n |
| 239 | "\\(?:-[{([<`\"].*?\\)?"; for ARG-{x}, (x), <x>, [x], `x' |
| 240 | "\\>") ; end of word |
| 241 | (help-highlight-arg arg) |
| 242 | doc t t 1))) |
| 243 | doc)) |
| 244 | |
| 245 | (defun help-highlight-arguments (usage doc &rest args) |
| 246 | (when (and usage (string-match "^(" usage)) |
| 247 | (with-temp-buffer |
| 248 | (insert usage) |
| 249 | (goto-char (point-min)) |
| 250 | (let ((case-fold-search nil) |
| 251 | (next (not (or args (looking-at "\\[")))) |
| 252 | (opt nil)) |
| 253 | ;; Make a list of all arguments |
| 254 | (skip-chars-forward "^ ") |
| 255 | (while next |
| 256 | (or opt (not (looking-at " &")) (setq opt t)) |
| 257 | (if (not (re-search-forward " \\([\\[(]*\\)\\([^] &)\.]+\\)" nil t)) |
| 258 | (setq next nil) |
| 259 | (setq args (cons (match-string 2) args)) |
| 260 | (when (and opt (string= (match-string 1) "(")) |
| 261 | ;; A pesky CL-style optional argument with default value, |
| 262 | ;; so let's skip over it |
| 263 | (search-backward "(") |
| 264 | (goto-char (scan-sexps (point) 1))))) |
| 265 | ;; Highlight arguments in the USAGE string |
| 266 | (setq usage (help-do-arg-highlight (buffer-string) args)) |
| 267 | ;; Highlight arguments in the DOC string |
| 268 | (setq doc (and doc (help-do-arg-highlight doc args)))))) |
| 269 | ;; Return value is like the one from help-split-fundoc, but highlighted |
| 270 | (cons usage doc)) |
| 271 | |
| 272 | ;; The following function was compiled from the former functions |
| 273 | ;; `describe-simplify-lib-file-name' and `find-source-lisp-file' with |
| 274 | ;; some excerpts from `describe-function-1' and `describe-variable'. |
| 275 | ;; The only additional twists provided are (1) locate the defining file |
| 276 | ;; for autoloaded functions, and (2) give preference to files in the |
| 277 | ;; "install directory" (directories found via `load-path') rather than |
| 278 | ;; to files in the "compile directory" (directories found by searching |
| 279 | ;; the loaddefs.el file). We autoload it because it's also used by |
| 280 | ;; `describe-face' (instead of `describe-simplify-lib-file-name'). |
| 281 | |
| 282 | ;;;###autoload |
| 283 | (defun find-lisp-object-file-name (object type) |
| 284 | "Guess the file that defined the Lisp object OBJECT, of type TYPE. |
| 285 | OBJECT should be a symbol associated with a function, variable, or face; |
| 286 | alternatively, it can be a function definition. |
| 287 | If TYPE is `defvar', search for a variable definition. |
| 288 | If TYPE is `defface', search for a face definition. |
| 289 | If TYPE is the value returned by `symbol-function' for a function symbol, |
| 290 | search for a function definition. |
| 291 | |
| 292 | The return value is the absolute name of a readable file where OBJECT is |
| 293 | defined. If several such files exist, preference is given to a file |
| 294 | found via `load-path'. The return value can also be `C-source', which |
| 295 | means that OBJECT is a function or variable defined in C. If no |
| 296 | suitable file is found, return nil." |
| 297 | (let* ((autoloaded (autoloadp type)) |
| 298 | (file-name (or (and autoloaded (nth 1 type)) |
| 299 | (symbol-file |
| 300 | object (if (memq type (list 'defvar 'defface)) |
| 301 | type |
| 302 | 'defun))))) |
| 303 | (cond |
| 304 | (autoloaded |
| 305 | ;; An autoloaded function: Locate the file since `symbol-function' |
| 306 | ;; has only returned a bare string here. |
| 307 | (setq file-name |
| 308 | (locate-file file-name load-path '(".el" ".elc") 'readable))) |
| 309 | ((and (stringp file-name) |
| 310 | (string-match "[.]*loaddefs.el\\'" file-name)) |
| 311 | ;; An autoloaded variable or face. Visit loaddefs.el in a buffer |
| 312 | ;; and try to extract the defining file. The following form is |
| 313 | ;; from `describe-function-1' and `describe-variable'. |
| 314 | (let ((location |
| 315 | (condition-case nil |
| 316 | (find-function-search-for-symbol object nil file-name) |
| 317 | (error nil)))) |
| 318 | (when (cdr location) |
| 319 | (with-current-buffer (car location) |
| 320 | (goto-char (cdr location)) |
| 321 | (when (re-search-backward |
| 322 | "^;;; Generated autoloads from \\(.*\\)" nil t) |
| 323 | (setq file-name |
| 324 | (locate-file |
| 325 | (file-name-sans-extension |
| 326 | (match-string-no-properties 1)) |
| 327 | load-path '(".el" ".elc") 'readable)))))))) |
| 328 | |
| 329 | (cond |
| 330 | ((and (not file-name) (subrp type)) |
| 331 | ;; A built-in function. The form is from `describe-function-1'. |
| 332 | (if (get-buffer " *DOC*") |
| 333 | (help-C-file-name type 'subr) |
| 334 | 'C-source)) |
| 335 | ((and (not file-name) (symbolp object) |
| 336 | (integerp (get object 'variable-documentation))) |
| 337 | ;; A variable defined in C. The form is from `describe-variable'. |
| 338 | (if (get-buffer " *DOC*") |
| 339 | (help-C-file-name object 'var) |
| 340 | 'C-source)) |
| 341 | ((not (stringp file-name)) |
| 342 | ;; If we don't have a file-name string by now, we lost. |
| 343 | nil) |
| 344 | ;; Now, `file-name' should have become an absolute file name. |
| 345 | ;; For files loaded from ~/.foo.elc, try ~/.foo. |
| 346 | ;; This applies to config files like ~/.emacs, |
| 347 | ;; which people sometimes compile. |
| 348 | ((let (fn) |
| 349 | (and (string-match "\\`\\..*\\.elc\\'" |
| 350 | (file-name-nondirectory file-name)) |
| 351 | (string-equal (file-name-directory file-name) |
| 352 | (file-name-as-directory (expand-file-name "~"))) |
| 353 | (file-readable-p (setq fn (file-name-sans-extension file-name))) |
| 354 | fn))) |
| 355 | ;; When the Elisp source file can be found in the install |
| 356 | ;; directory, return the name of that file. |
| 357 | ((let ((lib-name |
| 358 | (if (string-match "[.]elc\\'" file-name) |
| 359 | (substring-no-properties file-name 0 -1) |
| 360 | file-name))) |
| 361 | (or (and (file-readable-p lib-name) lib-name) |
| 362 | ;; The library might be compressed. |
| 363 | (and (file-readable-p (concat lib-name ".gz")) lib-name)))) |
| 364 | ((let* ((lib-name (file-name-nondirectory file-name)) |
| 365 | ;; The next form is from `describe-simplify-lib-file-name'. |
| 366 | (file-name |
| 367 | ;; Try converting the absolute file name to a library |
| 368 | ;; name, convert that back to a file name and see if we |
| 369 | ;; get the original one. If so, they are equivalent. |
| 370 | (if (equal file-name (locate-file lib-name load-path '(""))) |
| 371 | (if (string-match "[.]elc\\'" lib-name) |
| 372 | (substring-no-properties lib-name 0 -1) |
| 373 | lib-name) |
| 374 | file-name)) |
| 375 | ;; The next three forms are from `find-source-lisp-file'. |
| 376 | (elc-file (locate-file |
| 377 | (concat file-name |
| 378 | (if (string-match "\\.el\\'" file-name) |
| 379 | "c" |
| 380 | ".elc")) |
| 381 | load-path nil 'readable)) |
| 382 | (str (when elc-file |
| 383 | (with-temp-buffer |
| 384 | (insert-file-contents-literally elc-file nil 0 256) |
| 385 | (buffer-string)))) |
| 386 | (src-file (and str |
| 387 | (string-match ";;; from file \\(.*\\.el\\)" str) |
| 388 | (match-string 1 str)))) |
| 389 | (and src-file (file-readable-p src-file) src-file)))))) |
| 390 | |
| 391 | (defun help-fns--key-bindings (function) |
| 392 | (when (commandp function) |
| 393 | (let ((pt2 (with-current-buffer standard-output (point))) |
| 394 | (remapped (command-remapping function))) |
| 395 | (unless (memq remapped '(ignore undefined)) |
| 396 | (let ((keys (where-is-internal |
| 397 | (or remapped function) overriding-local-map nil nil)) |
| 398 | non-modified-keys) |
| 399 | (if (and (eq function 'self-insert-command) |
| 400 | (vectorp (car-safe keys)) |
| 401 | (consp (aref (car keys) 0))) |
| 402 | (princ "It is bound to many ordinary text characters.\n") |
| 403 | ;; Which non-control non-meta keys run this command? |
| 404 | (dolist (key keys) |
| 405 | (if (member (event-modifiers (aref key 0)) '(nil (shift))) |
| 406 | (push key non-modified-keys))) |
| 407 | (when remapped |
| 408 | (princ "Its keys are remapped to ") |
| 409 | (princ (if (symbolp remapped) |
| 410 | (concat "`" (symbol-name remapped) "'") |
| 411 | "an anonymous command")) |
| 412 | (princ ".\n")) |
| 413 | |
| 414 | (when keys |
| 415 | (princ (if remapped |
| 416 | "Without this remapping, it would be bound to " |
| 417 | "It is bound to ")) |
| 418 | ;; If lots of ordinary text characters run this command, |
| 419 | ;; don't mention them one by one. |
| 420 | (if (< (length non-modified-keys) 10) |
| 421 | (princ (mapconcat 'key-description keys ", ")) |
| 422 | (dolist (key non-modified-keys) |
| 423 | (setq keys (delq key keys))) |
| 424 | (if keys |
| 425 | (progn |
| 426 | (princ (mapconcat 'key-description keys ", ")) |
| 427 | (princ ", and many ordinary text characters")) |
| 428 | (princ "many ordinary text characters")))) |
| 429 | (when (or remapped keys non-modified-keys) |
| 430 | (princ ".") |
| 431 | (terpri))))) |
| 432 | |
| 433 | (with-current-buffer standard-output |
| 434 | (fill-region-as-paragraph pt2 (point)) |
| 435 | (unless (looking-back "\n\n") |
| 436 | (terpri)))))) |
| 437 | |
| 438 | (defun help-fns--compiler-macro (function) |
| 439 | (let ((handler (function-get function 'compiler-macro))) |
| 440 | (when handler |
| 441 | (insert "\nThis function has a compiler macro") |
| 442 | (if (symbolp handler) |
| 443 | (progn |
| 444 | (insert (format " `%s'" handler)) |
| 445 | (save-excursion |
| 446 | (re-search-backward "`\\([^`']+\\)'" nil t) |
| 447 | (help-xref-button 1 'help-function handler))) |
| 448 | ;; FIXME: Obsolete since 24.4. |
| 449 | (let ((lib (get function 'compiler-macro-file))) |
| 450 | (when (stringp lib) |
| 451 | (insert (format " in `%s'" lib)) |
| 452 | (save-excursion |
| 453 | (re-search-backward "`\\([^`']+\\)'" nil t) |
| 454 | (help-xref-button 1 'help-function-cmacro function lib))))) |
| 455 | (insert ".\n")))) |
| 456 | |
| 457 | (defun help-fns--signature (function doc real-def real-function) |
| 458 | (unless (keymapp function) ; If definition is a keymap, skip arglist note. |
| 459 | (let* ((advertised (gethash real-def advertised-signature-table t)) |
| 460 | (arglist (if (listp advertised) |
| 461 | advertised (help-function-arglist real-def))) |
| 462 | (usage (help-split-fundoc doc function))) |
| 463 | (if usage (setq doc (cdr usage))) |
| 464 | (let* ((use (cond |
| 465 | ((and usage (not (listp advertised))) (car usage)) |
| 466 | ((listp arglist) |
| 467 | (format "%S" (help-make-usage function arglist))) |
| 468 | ((stringp arglist) arglist) |
| 469 | ;; Maybe the arglist is in the docstring of a symbol |
| 470 | ;; this one is aliased to. |
| 471 | ((let ((fun real-function)) |
| 472 | (while (and (symbolp fun) |
| 473 | (setq fun (symbol-function fun)) |
| 474 | (not (setq usage (help-split-fundoc |
| 475 | (documentation fun) |
| 476 | function))))) |
| 477 | usage) |
| 478 | (car usage)) |
| 479 | ((or (stringp real-def) |
| 480 | (vectorp real-def)) |
| 481 | (format "\nMacro: %s" (format-kbd-macro real-def))) |
| 482 | (t "[Missing arglist. Please make a bug report.]"))) |
| 483 | (high (help-highlight-arguments use doc))) |
| 484 | (let ((fill-begin (point))) |
| 485 | (insert (car high) "\n") |
| 486 | (fill-region fill-begin (point))) |
| 487 | (cdr high))))) |
| 488 | |
| 489 | (defun help-fns--parent-mode (function) |
| 490 | ;; If this is a derived mode, link to the parent. |
| 491 | (let ((parent-mode (and (symbolp function) |
| 492 | (get function |
| 493 | 'derived-mode-parent)))) |
| 494 | (when parent-mode |
| 495 | (insert "\nParent mode: `") |
| 496 | (let ((beg (point))) |
| 497 | (insert (format "%s" parent-mode)) |
| 498 | (make-text-button beg (point) |
| 499 | 'type 'help-function |
| 500 | 'help-args (list parent-mode))) |
| 501 | (insert "'.\n")))) |
| 502 | |
| 503 | (defun help-fns--obsolete (function) |
| 504 | ;; Ignore lambda constructs, keyboard macros, etc. |
| 505 | (let* ((obsolete (and (symbolp function) |
| 506 | (get function 'byte-obsolete-info))) |
| 507 | (use (car obsolete))) |
| 508 | (when obsolete |
| 509 | (insert "\nThis " |
| 510 | (if (eq (car-safe (symbol-function function)) 'macro) |
| 511 | "macro" |
| 512 | "function") |
| 513 | " is obsolete") |
| 514 | (when (nth 2 obsolete) |
| 515 | (insert (format " since %s" (nth 2 obsolete)))) |
| 516 | (insert (cond ((stringp use) (concat ";\n" use)) |
| 517 | (use (format ";\nuse `%s' instead." use)) |
| 518 | (t ".")) |
| 519 | "\n")))) |
| 520 | |
| 521 | ;; We could use `symbol-file' but this is a wee bit more efficient. |
| 522 | (defun help-fns--autoloaded-p (function file) |
| 523 | "Return non-nil if FUNCTION has previously been autoloaded. |
| 524 | FILE is the file where FUNCTION was probably defined." |
| 525 | (let* ((file (file-name-sans-extension (file-truename file))) |
| 526 | (load-hist load-history) |
| 527 | (target (cons t function)) |
| 528 | found) |
| 529 | (while (and load-hist (not found)) |
| 530 | (and (caar load-hist) |
| 531 | (equal (file-name-sans-extension (caar load-hist)) file) |
| 532 | (setq found (member target (cdar load-hist)))) |
| 533 | (setq load-hist (cdr load-hist))) |
| 534 | found)) |
| 535 | |
| 536 | ;;;###autoload |
| 537 | (defun describe-function-1 (function) |
| 538 | (let* ((advised (and (symbolp function) |
| 539 | (featurep 'nadvice) |
| 540 | (advice--p (advice--symbol-function function)))) |
| 541 | ;; If the function is advised, use the symbol that has the |
| 542 | ;; real definition, if that symbol is already set up. |
| 543 | (real-function |
| 544 | (or (and advised |
| 545 | (advice--cd*r (advice--symbol-function function))) |
| 546 | function)) |
| 547 | ;; Get the real definition. |
| 548 | (def (if (symbolp real-function) |
| 549 | (symbol-function real-function) |
| 550 | real-function)) |
| 551 | (aliased (or (symbolp def) |
| 552 | ;; Advised & aliased function. |
| 553 | (and advised (symbolp real-function)))) |
| 554 | (real-def (cond |
| 555 | (aliased (let ((f real-function)) |
| 556 | (while (and (fboundp f) |
| 557 | (symbolp (symbol-function f))) |
| 558 | (setq f (symbol-function f))) |
| 559 | f)) |
| 560 | ((subrp def) (intern (subr-name def))) |
| 561 | (t def))) |
| 562 | (file-name (find-lisp-object-file-name function def)) |
| 563 | (pt1 (with-current-buffer (help-buffer) (point))) |
| 564 | (beg (if (and (or (byte-code-function-p def) |
| 565 | (keymapp def) |
| 566 | (memq (car-safe def) '(macro lambda closure))) |
| 567 | file-name |
| 568 | (help-fns--autoloaded-p function file-name)) |
| 569 | (if (commandp def) |
| 570 | "an interactive autoloaded " |
| 571 | "an autoloaded ") |
| 572 | (if (commandp def) "an interactive " "a ")))) |
| 573 | |
| 574 | ;; Print what kind of function-like object FUNCTION is. |
| 575 | (princ (cond ((or (stringp def) (vectorp def)) |
| 576 | "a keyboard macro") |
| 577 | ((subrp def) |
| 578 | (if (eq 'unevalled (cdr (subr-arity def))) |
| 579 | (concat beg "special form") |
| 580 | (concat beg "built-in function"))) |
| 581 | ;; Aliases are Lisp functions, so we need to check |
| 582 | ;; aliases before functions. |
| 583 | (aliased |
| 584 | (format "an alias for `%s'" real-def)) |
| 585 | ((or (eq (car-safe def) 'macro) |
| 586 | ;; For advised macros, def is a lambda |
| 587 | ;; expression or a byte-code-function-p, so we |
| 588 | ;; need to check macros before functions. |
| 589 | (macrop function)) |
| 590 | (concat beg "Lisp macro")) |
| 591 | ((byte-code-function-p def) |
| 592 | (concat beg "compiled Lisp function")) |
| 593 | ((eq (car-safe def) 'lambda) |
| 594 | (concat beg "Lisp function")) |
| 595 | ((eq (car-safe def) 'closure) |
| 596 | (concat beg "Lisp closure")) |
| 597 | ((autoloadp def) |
| 598 | (format "%s autoloaded %s" |
| 599 | (if (commandp def) "an interactive" "an") |
| 600 | (if (eq (nth 4 def) 'keymap) "keymap" |
| 601 | (if (nth 4 def) "Lisp macro" "Lisp function")))) |
| 602 | ((keymapp def) |
| 603 | (let ((is-full nil) |
| 604 | (elts (cdr-safe def))) |
| 605 | (while elts |
| 606 | (if (char-table-p (car-safe elts)) |
| 607 | (setq is-full t |
| 608 | elts nil)) |
| 609 | (setq elts (cdr-safe elts))) |
| 610 | (concat beg (if is-full "keymap" "sparse keymap")))) |
| 611 | (t ""))) |
| 612 | |
| 613 | (if (and aliased (not (fboundp real-def))) |
| 614 | (princ ",\nwhich is not defined. Please make a bug report.") |
| 615 | (with-current-buffer standard-output |
| 616 | (save-excursion |
| 617 | (save-match-data |
| 618 | (when (re-search-backward "alias for `\\([^`']+\\)'" nil t) |
| 619 | (help-xref-button 1 'help-function real-def))))) |
| 620 | |
| 621 | (when file-name |
| 622 | (princ " in `") |
| 623 | ;; We used to add .el to the file name, |
| 624 | ;; but that's completely wrong when the user used load-file. |
| 625 | (princ (if (eq file-name 'C-source) |
| 626 | "C source code" |
| 627 | (file-name-nondirectory file-name))) |
| 628 | (princ "'") |
| 629 | ;; Make a hyperlink to the library. |
| 630 | (with-current-buffer standard-output |
| 631 | (save-excursion |
| 632 | (re-search-backward "`\\([^`']+\\)'" nil t) |
| 633 | (help-xref-button 1 'help-function-def function file-name)))) |
| 634 | (princ ".") |
| 635 | (with-current-buffer (help-buffer) |
| 636 | (fill-region-as-paragraph (save-excursion (goto-char pt1) (forward-line 0) (point)) |
| 637 | (point))) |
| 638 | (terpri)(terpri) |
| 639 | |
| 640 | (let* ((doc-raw (documentation function t)) |
| 641 | ;; If the function is autoloaded, and its docstring has |
| 642 | ;; key substitution constructs, load the library. |
| 643 | (doc (progn |
| 644 | (and (autoloadp real-def) doc-raw |
| 645 | help-enable-auto-load |
| 646 | (string-match "\\([^\\]=\\|[^=]\\|\\`\\)\\\\[[{<]" |
| 647 | doc-raw) |
| 648 | (load (cadr real-def) t)) |
| 649 | (substitute-command-keys doc-raw)))) |
| 650 | |
| 651 | (help-fns--key-bindings function) |
| 652 | (with-current-buffer standard-output |
| 653 | (setq doc (help-fns--signature function doc real-def real-function)) |
| 654 | (run-hook-with-args 'help-fns-describe-function-functions function) |
| 655 | (insert "\n" |
| 656 | (or doc "Not documented."))))))) |
| 657 | |
| 658 | ;; Add defaults to `help-fns-describe-function-functions'. |
| 659 | (add-hook 'help-fns-describe-function-functions #'help-fns--obsolete) |
| 660 | (add-hook 'help-fns-describe-function-functions #'help-fns--parent-mode) |
| 661 | (add-hook 'help-fns-describe-function-functions #'help-fns--compiler-macro) |
| 662 | |
| 663 | \f |
| 664 | ;; Variables |
| 665 | |
| 666 | ;;;###autoload |
| 667 | (defun variable-at-point (&optional any-symbol) |
| 668 | "Return the bound variable symbol found at or before point. |
| 669 | Return 0 if there is no such symbol. |
| 670 | If ANY-SYMBOL is non-nil, don't insist the symbol be bound." |
| 671 | (with-syntax-table emacs-lisp-mode-syntax-table |
| 672 | (or (condition-case () |
| 673 | (save-excursion |
| 674 | (skip-chars-forward "'") |
| 675 | (or (not (zerop (skip-syntax-backward "_w"))) |
| 676 | (eq (char-syntax (following-char)) ?w) |
| 677 | (eq (char-syntax (following-char)) ?_) |
| 678 | (forward-sexp -1)) |
| 679 | (skip-chars-forward "'") |
| 680 | (let ((obj (read (current-buffer)))) |
| 681 | (and (symbolp obj) (boundp obj) obj))) |
| 682 | (error nil)) |
| 683 | (let* ((str (find-tag-default)) |
| 684 | (sym (if str (intern-soft str)))) |
| 685 | (if (and sym (or any-symbol (boundp sym))) |
| 686 | sym |
| 687 | (save-match-data |
| 688 | (when (and str (string-match "\\`\\W*\\(.*?\\)\\W*\\'" str)) |
| 689 | (setq sym (intern-soft (match-string 1 str))) |
| 690 | (and (or any-symbol (boundp sym)) sym))))) |
| 691 | 0))) |
| 692 | |
| 693 | (defun describe-variable-custom-version-info (variable) |
| 694 | (let ((custom-version (get variable 'custom-version)) |
| 695 | (cpv (get variable 'custom-package-version)) |
| 696 | (output nil)) |
| 697 | (if custom-version |
| 698 | (setq output |
| 699 | (format "This variable was introduced, or its default value was changed, in\nversion %s of Emacs.\n" |
| 700 | custom-version)) |
| 701 | (when cpv |
| 702 | (let* ((package (car-safe cpv)) |
| 703 | (version (if (listp (cdr-safe cpv)) |
| 704 | (car (cdr-safe cpv)) |
| 705 | (cdr-safe cpv))) |
| 706 | (pkg-versions (assq package customize-package-emacs-version-alist)) |
| 707 | (emacsv (cdr (assoc version pkg-versions)))) |
| 708 | (if (and package version) |
| 709 | (setq output |
| 710 | (format (concat "This variable was introduced, or its default value was changed, in\nversion %s of the %s package" |
| 711 | (if emacsv |
| 712 | (format " that is part of Emacs %s" emacsv)) |
| 713 | ".\n") |
| 714 | version package)))))) |
| 715 | output)) |
| 716 | |
| 717 | ;;;###autoload |
| 718 | (defun describe-variable (variable &optional buffer frame) |
| 719 | "Display the full documentation of VARIABLE (a symbol). |
| 720 | Returns the documentation as a string, also. |
| 721 | If VARIABLE has a buffer-local value in BUFFER or FRAME |
| 722 | \(default to the current buffer and current frame), |
| 723 | it is displayed along with the global value." |
| 724 | (interactive |
| 725 | (let ((v (variable-at-point)) |
| 726 | (enable-recursive-minibuffers t) |
| 727 | val) |
| 728 | (setq val (completing-read (if (symbolp v) |
| 729 | (format |
| 730 | "Describe variable (default %s): " v) |
| 731 | "Describe variable: ") |
| 732 | obarray |
| 733 | (lambda (vv) |
| 734 | (or (get vv 'variable-documentation) |
| 735 | (and (boundp vv) (not (keywordp vv))))) |
| 736 | t nil nil |
| 737 | (if (symbolp v) (symbol-name v)))) |
| 738 | (list (if (equal val "") |
| 739 | v (intern val))))) |
| 740 | (let (file-name) |
| 741 | (unless (buffer-live-p buffer) (setq buffer (current-buffer))) |
| 742 | (unless (frame-live-p frame) (setq frame (selected-frame))) |
| 743 | (if (not (symbolp variable)) |
| 744 | (message "You did not specify a variable") |
| 745 | (save-excursion |
| 746 | (let ((valvoid (not (with-current-buffer buffer (boundp variable)))) |
| 747 | (permanent-local (get variable 'permanent-local)) |
| 748 | val val-start-pos locus) |
| 749 | ;; Extract the value before setting up the output buffer, |
| 750 | ;; in case `buffer' *is* the output buffer. |
| 751 | (unless valvoid |
| 752 | (with-selected-frame frame |
| 753 | (with-current-buffer buffer |
| 754 | (setq val (symbol-value variable) |
| 755 | locus (variable-binding-locus variable))))) |
| 756 | (help-setup-xref (list #'describe-variable variable buffer) |
| 757 | (called-interactively-p 'interactive)) |
| 758 | (with-help-window (help-buffer) |
| 759 | (with-current-buffer buffer |
| 760 | (prin1 variable) |
| 761 | (setq file-name (find-lisp-object-file-name variable 'defvar)) |
| 762 | |
| 763 | (if file-name |
| 764 | (progn |
| 765 | (princ " is a variable defined in `") |
| 766 | (princ (if (eq file-name 'C-source) |
| 767 | "C source code" |
| 768 | (file-name-nondirectory file-name))) |
| 769 | (princ "'.\n") |
| 770 | (with-current-buffer standard-output |
| 771 | (save-excursion |
| 772 | (re-search-backward "`\\([^`']+\\)'" nil t) |
| 773 | (help-xref-button 1 'help-variable-def |
| 774 | variable file-name))) |
| 775 | (if valvoid |
| 776 | (princ "It is void as a variable.") |
| 777 | (princ "Its "))) |
| 778 | (if valvoid |
| 779 | (princ " is void as a variable.") |
| 780 | (princ "'s ")))) |
| 781 | (unless valvoid |
| 782 | (with-current-buffer standard-output |
| 783 | (setq val-start-pos (point)) |
| 784 | (princ "value is ") |
| 785 | (let ((from (point)) |
| 786 | (line-beg (line-beginning-position)) |
| 787 | (print-rep |
| 788 | (let ((print-quoted t)) |
| 789 | (prin1-to-string val)))) |
| 790 | (if (< (+ (length print-rep) (point) (- line-beg)) 68) |
| 791 | (insert print-rep) |
| 792 | (terpri) |
| 793 | (pp val) |
| 794 | (if (< (point) (+ 68 (line-beginning-position 0))) |
| 795 | (delete-region from (1+ from)) |
| 796 | (delete-region (1- from) from))) |
| 797 | (let* ((sv (get variable 'standard-value)) |
| 798 | (origval (and (consp sv) |
| 799 | (condition-case nil |
| 800 | (eval (car sv)) |
| 801 | (error :help-eval-error))))) |
| 802 | (when (and (consp sv) |
| 803 | (not (equal origval val)) |
| 804 | (not (equal origval :help-eval-error))) |
| 805 | (princ "\nOriginal value was \n") |
| 806 | (setq from (point)) |
| 807 | (pp origval) |
| 808 | (if (< (point) (+ from 20)) |
| 809 | (delete-region (1- from) from))))))) |
| 810 | (terpri) |
| 811 | (when locus |
| 812 | (cond |
| 813 | ((bufferp locus) |
| 814 | (princ (format "Local in buffer %s; " |
| 815 | (buffer-name buffer)))) |
| 816 | ((framep locus) |
| 817 | (princ (format "It is a frame-local variable; "))) |
| 818 | ((terminal-live-p locus) |
| 819 | (princ (format "It is a terminal-local variable; "))) |
| 820 | (t |
| 821 | (princ (format "It is local to %S" locus)))) |
| 822 | (if (not (default-boundp variable)) |
| 823 | (princ "globally void") |
| 824 | (let ((global-val (default-value variable))) |
| 825 | (with-current-buffer standard-output |
| 826 | (princ "global value is ") |
| 827 | (if (eq val global-val) |
| 828 | (princ "the same.") |
| 829 | (terpri) |
| 830 | ;; Fixme: pp can take an age if you happen to |
| 831 | ;; ask for a very large expression. We should |
| 832 | ;; probably print it raw once and check it's a |
| 833 | ;; sensible size before prettyprinting. -- fx |
| 834 | (let ((from (point))) |
| 835 | (pp global-val) |
| 836 | ;; See previous comment for this function. |
| 837 | ;; (help-xref-on-pp from (point)) |
| 838 | (if (< (point) (+ from 20)) |
| 839 | (delete-region (1- from) from))))))) |
| 840 | (terpri)) |
| 841 | |
| 842 | ;; If the value is large, move it to the end. |
| 843 | (with-current-buffer standard-output |
| 844 | (when (> (count-lines (point-min) (point-max)) 10) |
| 845 | ;; Note that setting the syntax table like below |
| 846 | ;; makes forward-sexp move over a `'s' at the end |
| 847 | ;; of a symbol. |
| 848 | (set-syntax-table emacs-lisp-mode-syntax-table) |
| 849 | (goto-char val-start-pos) |
| 850 | ;; The line below previously read as |
| 851 | ;; (delete-region (point) (progn (end-of-line) (point))) |
| 852 | ;; which suppressed display of the buffer local value for |
| 853 | ;; large values. |
| 854 | (when (looking-at "value is") (replace-match "")) |
| 855 | (save-excursion |
| 856 | (insert "\n\nValue:") |
| 857 | (set (make-local-variable 'help-button-cache) |
| 858 | (point-marker))) |
| 859 | (insert "value is shown ") |
| 860 | (insert-button "below" |
| 861 | 'action help-button-cache |
| 862 | 'follow-link t |
| 863 | 'help-echo "mouse-2, RET: show value") |
| 864 | (insert ".\n"))) |
| 865 | (terpri) |
| 866 | |
| 867 | (let* ((alias (condition-case nil |
| 868 | (indirect-variable variable) |
| 869 | (error variable))) |
| 870 | (obsolete (get variable 'byte-obsolete-variable)) |
| 871 | (use (car obsolete)) |
| 872 | (safe-var (get variable 'safe-local-variable)) |
| 873 | (doc (or (documentation-property |
| 874 | variable 'variable-documentation) |
| 875 | (documentation-property |
| 876 | alias 'variable-documentation))) |
| 877 | (extra-line nil)) |
| 878 | |
| 879 | ;; Mention if it's a local variable. |
| 880 | (cond |
| 881 | ((and (local-variable-if-set-p variable) |
| 882 | (or (not (local-variable-p variable)) |
| 883 | (with-temp-buffer |
| 884 | (local-variable-if-set-p variable)))) |
| 885 | (setq extra-line t) |
| 886 | (princ " Automatically becomes ") |
| 887 | (if permanent-local |
| 888 | (princ "permanently ")) |
| 889 | (princ "buffer-local when set.\n")) |
| 890 | ((not permanent-local)) |
| 891 | ((bufferp locus) |
| 892 | (setq extra-line t) |
| 893 | (princ " This variable's buffer-local value is permanent.\n")) |
| 894 | (t |
| 895 | (setq extra-line t) |
| 896 | (princ " This variable's value is permanent \ |
| 897 | if it is given a local binding.\n"))) |
| 898 | |
| 899 | ;; Mention if it's an alias. |
| 900 | (unless (eq alias variable) |
| 901 | (setq extra-line t) |
| 902 | (princ (format " This variable is an alias for `%s'.\n" alias))) |
| 903 | |
| 904 | (when obsolete |
| 905 | (setq extra-line t) |
| 906 | (princ " This variable is obsolete") |
| 907 | (if (nth 2 obsolete) |
| 908 | (princ (format " since %s" (nth 2 obsolete)))) |
| 909 | (princ (cond ((stringp use) (concat ";\n " use)) |
| 910 | (use (format ";\n use `%s' instead." (car obsolete))) |
| 911 | (t "."))) |
| 912 | (terpri)) |
| 913 | |
| 914 | (when (member (cons variable val) file-local-variables-alist) |
| 915 | (setq extra-line t) |
| 916 | (if (member (cons variable val) dir-local-variables-alist) |
| 917 | (let ((file (and (buffer-file-name) |
| 918 | (not (file-remote-p (buffer-file-name))) |
| 919 | (dir-locals-find-file |
| 920 | (buffer-file-name)))) |
| 921 | (dir-file t)) |
| 922 | (princ " This variable's value is directory-local") |
| 923 | (if (null file) |
| 924 | (princ ".\n") |
| 925 | (princ ", set ") |
| 926 | (if (consp file) ; result from cache |
| 927 | ;; If the cache element has an mtime, we |
| 928 | ;; assume it came from a file. |
| 929 | (if (nth 2 file) |
| 930 | (setq file (expand-file-name |
| 931 | dir-locals-file (car file))) |
| 932 | ;; Otherwise, assume it was set directly. |
| 933 | (setq dir-file nil))) |
| 934 | (princ (if dir-file |
| 935 | "by the file\n `" |
| 936 | "for the directory\n `")) |
| 937 | (with-current-buffer standard-output |
| 938 | (insert-text-button |
| 939 | file 'type 'help-dir-local-var-def |
| 940 | 'help-args (list variable file))) |
| 941 | (princ "'.\n"))) |
| 942 | (princ " This variable's value is file-local.\n"))) |
| 943 | |
| 944 | (when (memq variable ignored-local-variables) |
| 945 | (setq extra-line t) |
| 946 | (princ " This variable is ignored as a file-local \ |
| 947 | variable.\n")) |
| 948 | |
| 949 | ;; Can be both risky and safe, eg auto-fill-function. |
| 950 | (when (risky-local-variable-p variable) |
| 951 | (setq extra-line t) |
| 952 | (princ " This variable may be risky if used as a \ |
| 953 | file-local variable.\n") |
| 954 | (when (assq variable safe-local-variable-values) |
| 955 | (princ " However, you have added it to \ |
| 956 | `safe-local-variable-values'.\n"))) |
| 957 | |
| 958 | (when safe-var |
| 959 | (setq extra-line t) |
| 960 | (princ " This variable is safe as a file local variable ") |
| 961 | (princ "if its value\n satisfies the predicate ") |
| 962 | (princ (if (byte-code-function-p safe-var) |
| 963 | "which is a byte-compiled expression.\n" |
| 964 | (format "`%s'.\n" safe-var)))) |
| 965 | |
| 966 | (if extra-line (terpri)) |
| 967 | (princ "Documentation:\n") |
| 968 | (with-current-buffer standard-output |
| 969 | (insert (or doc "Not documented as a variable.")))) |
| 970 | |
| 971 | ;; Make a link to customize if this variable can be customized. |
| 972 | (when (custom-variable-p variable) |
| 973 | (let ((customize-label "customize")) |
| 974 | (terpri) |
| 975 | (terpri) |
| 976 | (princ (concat "You can " customize-label " this variable.")) |
| 977 | (with-current-buffer standard-output |
| 978 | (save-excursion |
| 979 | (re-search-backward |
| 980 | (concat "\\(" customize-label "\\)") nil t) |
| 981 | (help-xref-button 1 'help-customize-variable variable)))) |
| 982 | ;; Note variable's version or package version |
| 983 | (let ((output (describe-variable-custom-version-info variable))) |
| 984 | (when output |
| 985 | (terpri) |
| 986 | (terpri) |
| 987 | (princ output)))) |
| 988 | |
| 989 | (with-current-buffer standard-output |
| 990 | ;; Return the text we displayed. |
| 991 | (buffer-string)))))))) |
| 992 | |
| 993 | |
| 994 | ;;;###autoload |
| 995 | (defun describe-syntax (&optional buffer) |
| 996 | "Describe the syntax specifications in the syntax table of BUFFER. |
| 997 | The descriptions are inserted in a help buffer, which is then displayed. |
| 998 | BUFFER defaults to the current buffer." |
| 999 | (interactive) |
| 1000 | (setq buffer (or buffer (current-buffer))) |
| 1001 | (help-setup-xref (list #'describe-syntax buffer) |
| 1002 | (called-interactively-p 'interactive)) |
| 1003 | (with-help-window (help-buffer) |
| 1004 | (let ((table (with-current-buffer buffer (syntax-table)))) |
| 1005 | (with-current-buffer standard-output |
| 1006 | (describe-vector table 'internal-describe-syntax-value) |
| 1007 | (while (setq table (char-table-parent table)) |
| 1008 | (insert "\nThe parent syntax table is:") |
| 1009 | (describe-vector table 'internal-describe-syntax-value)))))) |
| 1010 | |
| 1011 | (defun help-describe-category-set (value) |
| 1012 | (insert (cond |
| 1013 | ((null value) "default") |
| 1014 | ((char-table-p value) "deeper char-table ...") |
| 1015 | (t (condition-case nil |
| 1016 | (category-set-mnemonics value) |
| 1017 | (error "invalid")))))) |
| 1018 | |
| 1019 | ;;;###autoload |
| 1020 | (defun describe-categories (&optional buffer) |
| 1021 | "Describe the category specifications in the current category table. |
| 1022 | The descriptions are inserted in a buffer, which is then displayed. |
| 1023 | If BUFFER is non-nil, then describe BUFFER's category table instead. |
| 1024 | BUFFER should be a buffer or a buffer name." |
| 1025 | (interactive) |
| 1026 | (setq buffer (or buffer (current-buffer))) |
| 1027 | (help-setup-xref (list #'describe-categories buffer) |
| 1028 | (called-interactively-p 'interactive)) |
| 1029 | (with-help-window (help-buffer) |
| 1030 | (let* ((table (with-current-buffer buffer (category-table))) |
| 1031 | (docs (char-table-extra-slot table 0))) |
| 1032 | (if (or (not (vectorp docs)) (/= (length docs) 95)) |
| 1033 | (error "Invalid first extra slot in this category table\n")) |
| 1034 | (with-current-buffer standard-output |
| 1035 | (insert "Legend of category mnemonics (see the tail for the longer description)\n") |
| 1036 | (let ((pos (point)) (items 0) lines n) |
| 1037 | (dotimes (i 95) |
| 1038 | (if (aref docs i) (setq items (1+ items)))) |
| 1039 | (setq lines (1+ (/ (1- items) 4))) |
| 1040 | (setq n 0) |
| 1041 | (dotimes (i 95) |
| 1042 | (let ((elt (aref docs i))) |
| 1043 | (when elt |
| 1044 | (string-match ".*" elt) |
| 1045 | (setq elt (match-string 0 elt)) |
| 1046 | (if (>= (length elt) 17) |
| 1047 | (setq elt (concat (substring elt 0 14) "..."))) |
| 1048 | (if (< (point) (point-max)) |
| 1049 | (move-to-column (* 20 (/ n lines)) t)) |
| 1050 | (insert (+ i ?\s) ?: elt) |
| 1051 | (if (< (point) (point-max)) |
| 1052 | (forward-line 1) |
| 1053 | (insert "\n")) |
| 1054 | (setq n (1+ n)) |
| 1055 | (if (= (% n lines) 0) |
| 1056 | (goto-char pos)))))) |
| 1057 | (goto-char (point-max)) |
| 1058 | (insert "\n" |
| 1059 | "character(s)\tcategory mnemonics\n" |
| 1060 | "------------\t------------------") |
| 1061 | (describe-vector table 'help-describe-category-set) |
| 1062 | (insert "Legend of category mnemonics:\n") |
| 1063 | (dotimes (i 95) |
| 1064 | (let ((elt (aref docs i))) |
| 1065 | (when elt |
| 1066 | (if (string-match "\n" elt) |
| 1067 | (setq elt (substring elt (match-end 0)))) |
| 1068 | (insert (+ i ?\s) ": " elt "\n")))) |
| 1069 | (while (setq table (char-table-parent table)) |
| 1070 | (insert "\nThe parent category table is:") |
| 1071 | (describe-vector table 'help-describe-category-set)))))) |
| 1072 | |
| 1073 | \f |
| 1074 | ;;; Replacements for old lib-src/ programs. Don't seem especially useful. |
| 1075 | |
| 1076 | ;; Replaces lib-src/digest-doc.c. |
| 1077 | ;;;###autoload |
| 1078 | (defun doc-file-to-man (file) |
| 1079 | "Produce an nroff buffer containing the doc-strings from the DOC file." |
| 1080 | (interactive (list (read-file-name "Name of DOC file: " doc-directory |
| 1081 | internal-doc-file-name t))) |
| 1082 | (or (file-readable-p file) |
| 1083 | (error "Cannot read file `%s'" file)) |
| 1084 | (pop-to-buffer (generate-new-buffer "*man-doc*")) |
| 1085 | (setq buffer-undo-list t) |
| 1086 | (insert ".TH \"Command Summary for GNU Emacs\"\n" |
| 1087 | ".AU Richard M. Stallman\n") |
| 1088 | (insert-file-contents file) |
| 1089 | (let (notfirst) |
| 1090 | (while (search-forward "\1f" nil 'move) |
| 1091 | (if (looking-at "S") |
| 1092 | (delete-region (1- (point)) (line-end-position)) |
| 1093 | (delete-char -1) |
| 1094 | (if notfirst |
| 1095 | (insert "\n.DE\n") |
| 1096 | (setq notfirst t)) |
| 1097 | (insert "\n.SH ") |
| 1098 | (insert (if (looking-at "F") "Function " "Variable ")) |
| 1099 | (delete-char 1) |
| 1100 | (forward-line 1) |
| 1101 | (insert ".DS L\n")))) |
| 1102 | (insert "\n.DE\n") |
| 1103 | (setq buffer-undo-list nil) |
| 1104 | (nroff-mode)) |
| 1105 | |
| 1106 | ;; Replaces lib-src/sorted-doc.c. |
| 1107 | ;;;###autoload |
| 1108 | (defun doc-file-to-info (file) |
| 1109 | "Produce a texinfo buffer with sorted doc-strings from the DOC file." |
| 1110 | (interactive (list (read-file-name "Name of DOC file: " doc-directory |
| 1111 | internal-doc-file-name t))) |
| 1112 | (or (file-readable-p file) |
| 1113 | (error "Cannot read file `%s'" file)) |
| 1114 | (let ((i 0) type name doc alist) |
| 1115 | (with-temp-buffer |
| 1116 | (insert-file-contents file) |
| 1117 | ;; The characters "@{}" need special treatment. |
| 1118 | (while (re-search-forward "[@{}]" nil t) |
| 1119 | (backward-char) |
| 1120 | (insert "@") |
| 1121 | (forward-char 1)) |
| 1122 | (goto-char (point-min)) |
| 1123 | (while (search-forward "\1f" nil t) |
| 1124 | (unless (looking-at "S") |
| 1125 | (setq type (char-after) |
| 1126 | name (buffer-substring (1+ (point)) (line-end-position)) |
| 1127 | doc (buffer-substring (line-beginning-position 2) |
| 1128 | (if (search-forward "\1f" nil 'move) |
| 1129 | (1- (point)) |
| 1130 | (point))) |
| 1131 | alist (cons (list name type doc) alist)) |
| 1132 | (backward-char 1)))) |
| 1133 | (pop-to-buffer (generate-new-buffer "*info-doc*")) |
| 1134 | (setq buffer-undo-list t) |
| 1135 | ;; Write the output header. |
| 1136 | (insert "\\input texinfo @c -*-texinfo-*-\n" |
| 1137 | "@setfilename emacsdoc.info\n" |
| 1138 | "@settitle Command Summary for GNU Emacs\n" |
| 1139 | "@finalout\n" |
| 1140 | "\n@node Top\n" |
| 1141 | "@unnumbered Command Summary for GNU Emacs\n\n" |
| 1142 | "@table @asis\n\n" |
| 1143 | "@iftex\n" |
| 1144 | "@global@let@ITEM@item\n" |
| 1145 | "@def@item{@filbreak@vskip5pt@ITEM}\n" |
| 1146 | "@font@tensy cmsy10 scaled @magstephalf\n" |
| 1147 | "@font@teni cmmi10 scaled @magstephalf\n" |
| 1148 | "@def\\{{@tensy@char110}}\n" ; this backslash goes with cmr10 |
| 1149 | "@def|{{@tensy@char106}}\n" |
| 1150 | "@def@{{{@tensy@char102}}\n" |
| 1151 | "@def@}{{@tensy@char103}}\n" |
| 1152 | "@def<{{@teni@char62}}\n" |
| 1153 | "@def>{{@teni@char60}}\n" |
| 1154 | "@chardef@@64\n" |
| 1155 | "@catcode43=12\n" |
| 1156 | "@tableindent-0.2in\n" |
| 1157 | "@end iftex\n") |
| 1158 | ;; Sort the array by name; within each name, by type (functions first). |
| 1159 | (setq alist (sort alist (lambda (e1 e2) |
| 1160 | (if (string-equal (car e1) (car e2)) |
| 1161 | (<= (cadr e1) (cadr e2)) |
| 1162 | (string-lessp (car e1) (car e2)))))) |
| 1163 | ;; Print each function. |
| 1164 | (dolist (e alist) |
| 1165 | (insert "\n@item " |
| 1166 | (if (char-equal (cadr e) ?\F) "Function" "Variable") |
| 1167 | " @code{" (car e) "}\n@display\n" |
| 1168 | (nth 2 e) |
| 1169 | "\n@end display\n") |
| 1170 | ;; Try to avoid a save size overflow in the TeX output routine. |
| 1171 | (if (zerop (setq i (% (1+ i) 100))) |
| 1172 | (insert "\n@end table\n@table @asis\n"))) |
| 1173 | (insert "@end table\n" |
| 1174 | "@bye\n") |
| 1175 | (setq buffer-undo-list nil) |
| 1176 | (texinfo-mode))) |
| 1177 | |
| 1178 | (provide 'help-fns) |
| 1179 | |
| 1180 | ;;; help-fns.el ends here |