| 1 | ;;; help-fns.el --- Complex help functions |
| 2 | |
| 3 | ;; Copyright (C) 1985, 86, 93, 94, 98, 1999, 2000, 01, 02, 03, 2004 |
| 4 | ;; Free Software Foundation, Inc. |
| 5 | |
| 6 | ;; Maintainer: FSF |
| 7 | ;; Keywords: help, internal |
| 8 | |
| 9 | ;; This file is part of GNU Emacs. |
| 10 | |
| 11 | ;; GNU Emacs is free software; you can redistribute it and/or modify |
| 12 | ;; it under the terms of the GNU General Public License as published by |
| 13 | ;; the Free Software Foundation; either version 2, or (at your option) |
| 14 | ;; any later version. |
| 15 | |
| 16 | ;; GNU Emacs is distributed in the hope that it will be useful, |
| 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 19 | ;; GNU General Public License for more details. |
| 20 | |
| 21 | ;; You should have received a copy of the GNU General Public License |
| 22 | ;; along with GNU Emacs; see the file COPYING. If not, write to the |
| 23 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| 24 | ;; Boston, MA 02111-1307, USA. |
| 25 | |
| 26 | ;;; Commentary: |
| 27 | |
| 28 | ;; This file contains those help commands which are complicated, and |
| 29 | ;; which may not be used in every session. For example |
| 30 | ;; `describe-function' will probably be heavily used when doing elisp |
| 31 | ;; programming, but not if just editing C files. Simpler help commands |
| 32 | ;; are in help.el |
| 33 | |
| 34 | ;;; Code: |
| 35 | |
| 36 | (require 'help-mode) |
| 37 | |
| 38 | |
| 39 | ;;;###autoload |
| 40 | (defun help-with-tutorial (&optional arg) |
| 41 | "Select the Emacs learn-by-doing tutorial. |
| 42 | If there is a tutorial version written in the language |
| 43 | of the selected language environment, that version is used. |
| 44 | If there's no tutorial in that language, `TUTORIAL' is selected. |
| 45 | With ARG, you are asked to choose which language." |
| 46 | (interactive "P") |
| 47 | (let ((lang (if arg |
| 48 | (let ((minibuffer-setup-hook minibuffer-setup-hook)) |
| 49 | (add-hook 'minibuffer-setup-hook |
| 50 | 'minibuffer-completion-help) |
| 51 | (read-language-name 'tutorial "Language: " "English")) |
| 52 | (if (get-language-info current-language-environment 'tutorial) |
| 53 | current-language-environment |
| 54 | "English"))) |
| 55 | file filename) |
| 56 | (setq filename (get-language-info lang 'tutorial)) |
| 57 | (setq file (expand-file-name (concat "~/" filename))) |
| 58 | (delete-other-windows) |
| 59 | (if (get-file-buffer file) |
| 60 | (switch-to-buffer (get-file-buffer file)) |
| 61 | (switch-to-buffer (create-file-buffer file)) |
| 62 | (setq buffer-file-name file) |
| 63 | (setq default-directory (expand-file-name "~/")) |
| 64 | (setq buffer-auto-save-file-name nil) |
| 65 | (insert-file-contents (expand-file-name filename data-directory)) |
| 66 | (hack-local-variables) |
| 67 | (goto-char (point-min)) |
| 68 | (search-forward "\n<<") |
| 69 | (beginning-of-line) |
| 70 | ;; Convert the <<...>> line to the proper [...] line, |
| 71 | ;; or just delete the <<...>> line if a [...] line follows. |
| 72 | (cond ((save-excursion |
| 73 | (forward-line 1) |
| 74 | (looking-at "\\[")) |
| 75 | (delete-region (point) (progn (forward-line 1) (point)))) |
| 76 | ((looking-at "<<Blank lines inserted.*>>") |
| 77 | (replace-match "[Middle of page left blank for didactic purposes. Text continues below]")) |
| 78 | (t |
| 79 | (looking-at "<<") |
| 80 | (replace-match "[") |
| 81 | (search-forward ">>") |
| 82 | (replace-match "]"))) |
| 83 | (beginning-of-line) |
| 84 | (let ((n (- (window-height (selected-window)) |
| 85 | (count-lines (point-min) (point)) |
| 86 | 6))) |
| 87 | (if (< n 8) |
| 88 | (progn |
| 89 | ;; For a short gap, we don't need the [...] line, |
| 90 | ;; so delete it. |
| 91 | (delete-region (point) (progn (end-of-line) (point))) |
| 92 | (newline n)) |
| 93 | ;; Some people get confused by the large gap. |
| 94 | (newline (/ n 2)) |
| 95 | |
| 96 | ;; Skip the [...] line (don't delete it). |
| 97 | (forward-line 1) |
| 98 | (newline (- n (/ n 2))))) |
| 99 | (goto-char (point-min)) |
| 100 | (set-buffer-modified-p nil)))) |
| 101 | |
| 102 | ;;;###autoload |
| 103 | (defun locate-library (library &optional nosuffix path interactive-call) |
| 104 | "Show the precise file name of Emacs library LIBRARY. |
| 105 | This command searches the directories in `load-path' like `\\[load-library]' |
| 106 | to find the file that `\\[load-library] RET LIBRARY RET' would load. |
| 107 | Optional second arg NOSUFFIX non-nil means don't add suffixes `load-suffixes' |
| 108 | to the specified name LIBRARY. |
| 109 | |
| 110 | If the optional third arg PATH is specified, that list of directories |
| 111 | is used instead of `load-path'. |
| 112 | |
| 113 | When called from a program, the file name is normaly returned as a |
| 114 | string. When run interactively, the argument INTERACTIVE-CALL is t, |
| 115 | and the file name is displayed in the echo area." |
| 116 | (interactive (list (completing-read "Locate library: " |
| 117 | 'locate-file-completion |
| 118 | (cons load-path load-suffixes)) |
| 119 | nil nil |
| 120 | t)) |
| 121 | (let ((file (locate-file library |
| 122 | (or path load-path) |
| 123 | (append (unless nosuffix load-suffixes) '(""))))) |
| 124 | (if interactive-call |
| 125 | (if file |
| 126 | (message "Library is file %s" (abbreviate-file-name file)) |
| 127 | (message "No library %s in search path" library))) |
| 128 | file)) |
| 129 | |
| 130 | \f |
| 131 | ;; Functions |
| 132 | |
| 133 | ;;;###autoload |
| 134 | (defun describe-function (function) |
| 135 | "Display the full documentation of FUNCTION (a symbol)." |
| 136 | (interactive |
| 137 | (let ((fn (function-called-at-point)) |
| 138 | (enable-recursive-minibuffers t) |
| 139 | val) |
| 140 | (setq val (completing-read (if fn |
| 141 | (format "Describe function (default %s): " fn) |
| 142 | "Describe function: ") |
| 143 | obarray 'fboundp t nil nil (symbol-name fn))) |
| 144 | (list (if (equal val "") |
| 145 | fn (intern val))))) |
| 146 | (if (null function) |
| 147 | (message "You didn't specify a function") |
| 148 | (help-setup-xref (list #'describe-function function) (interactive-p)) |
| 149 | (save-excursion |
| 150 | (with-output-to-temp-buffer (help-buffer) |
| 151 | (prin1 function) |
| 152 | ;; Use " is " instead of a colon so that |
| 153 | ;; it is easier to get out the function name using forward-sexp. |
| 154 | (princ " is ") |
| 155 | (describe-function-1 function) |
| 156 | (print-help-return-message) |
| 157 | (with-current-buffer standard-output |
| 158 | ;; Return the text we displayed. |
| 159 | (buffer-string)))))) |
| 160 | |
| 161 | (defun help-split-fundoc (docstring def) |
| 162 | "Split a function DOCSTRING into the actual doc and the usage info. |
| 163 | Return (USAGE . DOC) or nil if there's no usage info. |
| 164 | DEF is the function whose usage we're looking for in DOCSTRING." |
| 165 | ;; Functions can get the calling sequence at the end of the doc string. |
| 166 | ;; In cases where `function' has been fset to a subr we can't search for |
| 167 | ;; function's name in the doc string so we use `fn' as the anonymous |
| 168 | ;; function name instead. |
| 169 | (when (and docstring (string-match "\n\n(fn\\(\\( .*\\)?)\\)\\'" docstring)) |
| 170 | (cons (format "(%s%s" |
| 171 | ;; Replace `fn' with the actual function name. |
| 172 | (if (consp def) "anonymous" def) |
| 173 | (match-string 1 docstring)) |
| 174 | (substring docstring 0 (match-beginning 0))))) |
| 175 | |
| 176 | (defun help-add-fundoc-usage (docstring arglist) |
| 177 | "Add the usage info to DOCSTRING. |
| 178 | If DOCSTRING already has a usage info, then just return it unchanged. |
| 179 | The usage info is built from ARGLIST. DOCSTRING can be nil. |
| 180 | ARGLIST can also be t or a string of the form \"(FUN ARG1 ARG2 ...)\"." |
| 181 | (unless (stringp docstring) (setq docstring "Not documented")) |
| 182 | (if (or (string-match "\n\n(fn\\(\\( .*\\)?)\\)\\'" docstring) (eq arglist t)) |
| 183 | docstring |
| 184 | (concat docstring |
| 185 | (if (string-match "\n?\n\\'" docstring) |
| 186 | (if (< (- (match-end 0) (match-beginning 0)) 2) "\n" "") |
| 187 | "\n\n") |
| 188 | (if (and (stringp arglist) |
| 189 | (string-match "\\`([^ ]+\\(.*\\))\\'" arglist)) |
| 190 | (concat "(fn" (match-string 1 arglist) ")") |
| 191 | (format "%S" (help-make-usage 'fn arglist)))))) |
| 192 | |
| 193 | (defun help-function-arglist (def) |
| 194 | ;; Handle symbols aliased to other symbols. |
| 195 | (if (and (symbolp def) (fboundp def)) (setq def (indirect-function def))) |
| 196 | ;; If definition is a macro, find the function inside it. |
| 197 | (if (eq (car-safe def) 'macro) (setq def (cdr def))) |
| 198 | (cond |
| 199 | ((byte-code-function-p def) (aref def 0)) |
| 200 | ((eq (car-safe def) 'lambda) (nth 1 def)) |
| 201 | ((and (eq (car-safe def) 'autoload) (not (eq (nth 4 def) 'keymap))) |
| 202 | "[Arg list not available until function definition is loaded.]") |
| 203 | (t t))) |
| 204 | |
| 205 | (defun help-make-usage (function arglist) |
| 206 | (cons (if (symbolp function) function 'anonymous) |
| 207 | (mapcar (lambda (arg) |
| 208 | (if (not (symbolp arg)) |
| 209 | (if (and (consp arg) (symbolp (car arg))) |
| 210 | ;; CL style default values for optional args. |
| 211 | (cons (intern (upcase (symbol-name (car arg)))) |
| 212 | (cdr arg)) |
| 213 | arg) |
| 214 | (let ((name (symbol-name arg))) |
| 215 | (if (string-match "\\`&" name) arg |
| 216 | (intern (upcase name)))))) |
| 217 | arglist))) |
| 218 | |
| 219 | (defun help-C-file-name (subr-or-var kind) |
| 220 | "Return the name of the C file where SUBR-OR-VAR is defined. |
| 221 | KIND should be `var' for a variable or `subr' for a subroutine." |
| 222 | (let ((docbuf (get-buffer-create " *DOC*")) |
| 223 | (name (if (eq 'var kind) |
| 224 | (concat "V" (symbol-name subr-or-var)) |
| 225 | (concat "F" (subr-name subr-or-var))))) |
| 226 | (with-current-buffer docbuf |
| 227 | (goto-char (point-min)) |
| 228 | (if (eobp) |
| 229 | (insert-file-contents-literally |
| 230 | (expand-file-name internal-doc-file-name doc-directory))) |
| 231 | (let ((file (catch 'loop |
| 232 | (while t |
| 233 | (let ((pnt (search-forward (concat "\1f" name "\n")))) |
| 234 | (re-search-backward "\1fS\\(.*\\)") |
| 235 | (let ((file (match-string 1))) |
| 236 | (if (member file build-files) |
| 237 | (throw 'loop file) |
| 238 | (goto-char pnt)))))))) |
| 239 | (if (string-match "\\.\\(o\\|obj\\)\\'" file) |
| 240 | (setq file (replace-match ".c" t t file))) |
| 241 | (if (string-match "\\.c\\'" file) |
| 242 | (concat "src/" file) |
| 243 | file))))) |
| 244 | |
| 245 | ;;;###autoload |
| 246 | (defface help-argument-name '((((supports :slant italic)) :inherit italic)) |
| 247 | "Face to highlight argument names in *Help* buffers." |
| 248 | :group 'help) |
| 249 | |
| 250 | (defun help-default-arg-highlight (arg) |
| 251 | "Default function to highlight arguments in *Help* buffers. |
| 252 | It returns ARG in face `help-argument-name'; ARG is also |
| 253 | downcased if it displays differently than the default |
| 254 | face (according to `face-differs-from-default-p')." |
| 255 | (propertize (if (face-differs-from-default-p 'help-argument-name) |
| 256 | (downcase arg) |
| 257 | arg) |
| 258 | 'face 'help-argument-name)) |
| 259 | |
| 260 | (defun help-do-arg-highlight (doc args) |
| 261 | (with-syntax-table (make-syntax-table emacs-lisp-mode-syntax-table) |
| 262 | (modify-syntax-entry ?\- "w") |
| 263 | (while args |
| 264 | (let ((arg (prog1 (car args) (setq args (cdr args))))) |
| 265 | (setq doc (replace-regexp-in-string |
| 266 | ;; This is heuristic, but covers all common cases |
| 267 | ;; except ARG1-ARG2 |
| 268 | (concat "\\<" ; beginning of word |
| 269 | "\\(?:[a-z-]+-\\)?" ; for xxx-ARG |
| 270 | "\\(" |
| 271 | arg |
| 272 | "\\)" |
| 273 | "\\(?:es\\|s\\|th\\)?" ; for ARGth, ARGs |
| 274 | "\\(?:-[a-z-]+\\)?" ; for ARG-xxx |
| 275 | "\\>") ; end of word |
| 276 | (help-default-arg-highlight arg) |
| 277 | doc t t 1)))) |
| 278 | doc)) |
| 279 | |
| 280 | (defun help-highlight-arguments (usage doc &rest args) |
| 281 | (when usage |
| 282 | (with-temp-buffer |
| 283 | (insert usage) |
| 284 | (goto-char (point-min)) |
| 285 | (let ((case-fold-search nil) |
| 286 | (next (not (or args (looking-at "\\[")))) |
| 287 | (opt nil)) |
| 288 | ;; Make a list of all arguments |
| 289 | (skip-chars-forward "^ ") |
| 290 | (while next |
| 291 | (or opt (not (looking-at " &")) (setq opt t)) |
| 292 | (if (not (re-search-forward " \\([\\[(]*\\)\\([^] &)\.]+\\)" nil t)) |
| 293 | (setq next nil) |
| 294 | (setq args (cons (match-string 2) args)) |
| 295 | (when (and opt (string= (match-string 1) "(")) |
| 296 | ;; A pesky CL-style optional argument with default value, |
| 297 | ;; so let's skip over it |
| 298 | (search-backward "(") |
| 299 | (goto-char (scan-sexps (point) 1))))) |
| 300 | ;; Highlight aguments in the USAGE string |
| 301 | (setq usage (help-do-arg-highlight (buffer-string) args)) |
| 302 | ;; Highlight arguments in the DOC string |
| 303 | (setq doc (and doc (help-do-arg-highlight doc args)))))) |
| 304 | ;; Return value is like the one from help-split-fundoc, but highlighted |
| 305 | (cons usage doc)) |
| 306 | |
| 307 | ;;;###autoload |
| 308 | (defun describe-function-1 (function) |
| 309 | (let* ((def (if (symbolp function) |
| 310 | (symbol-function function) |
| 311 | function)) |
| 312 | file-name string |
| 313 | (beg (if (commandp def) "an interactive " "a "))) |
| 314 | (setq string |
| 315 | (cond ((or (stringp def) |
| 316 | (vectorp def)) |
| 317 | "a keyboard macro") |
| 318 | ((subrp def) |
| 319 | (if (eq 'unevalled (cdr (subr-arity def))) |
| 320 | (concat beg "special form") |
| 321 | (concat beg "built-in function"))) |
| 322 | ((byte-code-function-p def) |
| 323 | (concat beg "compiled Lisp function")) |
| 324 | ((symbolp def) |
| 325 | (while (symbolp (symbol-function def)) |
| 326 | (setq def (symbol-function def))) |
| 327 | (format "an alias for `%s'" def)) |
| 328 | ((eq (car-safe def) 'lambda) |
| 329 | (concat beg "Lisp function")) |
| 330 | ((eq (car-safe def) 'macro) |
| 331 | "a Lisp macro") |
| 332 | ((eq (car-safe def) 'autoload) |
| 333 | (setq file-name (nth 1 def)) |
| 334 | (format "%s autoloaded %s" |
| 335 | (if (commandp def) "an interactive" "an") |
| 336 | (if (eq (nth 4 def) 'keymap) "keymap" |
| 337 | (if (nth 4 def) "Lisp macro" "Lisp function")) |
| 338 | )) |
| 339 | ((keymapp def) |
| 340 | (let ((is-full nil) |
| 341 | (elts (cdr-safe def))) |
| 342 | (while elts |
| 343 | (if (char-table-p (car-safe elts)) |
| 344 | (setq is-full t |
| 345 | elts nil)) |
| 346 | (setq elts (cdr-safe elts))) |
| 347 | (if is-full |
| 348 | "a full keymap" |
| 349 | "a sparse keymap"))) |
| 350 | (t ""))) |
| 351 | (princ string) |
| 352 | (with-current-buffer standard-output |
| 353 | (save-excursion |
| 354 | (save-match-data |
| 355 | (if (re-search-backward "alias for `\\([^`']+\\)'" nil t) |
| 356 | (help-xref-button 1 'help-function def))))) |
| 357 | (or file-name |
| 358 | (setq file-name (symbol-file function))) |
| 359 | (when (equal file-name "loaddefs.el") |
| 360 | ;; Find the real def site of the preloaded function. |
| 361 | ;; This is necessary only for defaliases. |
| 362 | (let ((location |
| 363 | (condition-case nil |
| 364 | (find-function-search-for-symbol function nil "loaddefs.el") |
| 365 | (error nil)))) |
| 366 | (when location |
| 367 | (with-current-buffer (car location) |
| 368 | (goto-char (cdr location)) |
| 369 | (when (re-search-backward |
| 370 | "^;;; Generated autoloads from \\(.*\\)" nil t) |
| 371 | (setq file-name (match-string 1))))))) |
| 372 | (when (and (null file-name) (subrp def)) |
| 373 | ;; Find the C source file name. |
| 374 | (setq file-name (if (get-buffer " *DOC*") |
| 375 | (help-C-file-name def 'subr) |
| 376 | 'C-source))) |
| 377 | (when file-name |
| 378 | (princ " in `") |
| 379 | ;; We used to add .el to the file name, |
| 380 | ;; but that's completely wrong when the user used load-file. |
| 381 | (princ (if (eq file-name 'C-source) "C source code" file-name)) |
| 382 | (princ "'") |
| 383 | ;; Make a hyperlink to the library. |
| 384 | (with-current-buffer standard-output |
| 385 | (save-excursion |
| 386 | (re-search-backward "`\\([^`']+\\)'" nil t) |
| 387 | (help-xref-button 1 'help-function-def function file-name)))) |
| 388 | (princ ".") |
| 389 | (terpri) |
| 390 | (when (commandp function) |
| 391 | (let* ((remapped (command-remapping function)) |
| 392 | (keys (where-is-internal |
| 393 | (or remapped function) overriding-local-map nil nil)) |
| 394 | non-modified-keys) |
| 395 | ;; Which non-control non-meta keys run this command? |
| 396 | (dolist (key keys) |
| 397 | (if (member (event-modifiers (aref key 0)) '(nil (shift))) |
| 398 | (push key non-modified-keys))) |
| 399 | (when remapped |
| 400 | (princ "It is remapped to `") |
| 401 | (princ (symbol-name remapped)) |
| 402 | (princ "'")) |
| 403 | |
| 404 | (when keys |
| 405 | (princ (if remapped " which is bound to " "It is bound to ")) |
| 406 | ;; FIXME: This list can be very long (f.ex. for self-insert-command). |
| 407 | ;; If there are many, remove them from KEYS. |
| 408 | (if (< (length non-modified-keys) 10) |
| 409 | (princ (mapconcat 'key-description keys ", ")) |
| 410 | (dolist (key non-modified-keys) |
| 411 | (setq keys (delq key keys))) |
| 412 | (if keys |
| 413 | (progn |
| 414 | (princ (mapconcat 'key-description keys ", ")) |
| 415 | (princ ", and many ordinary text characters")) |
| 416 | (princ "many ordinary text characters")))) |
| 417 | (when (or remapped keys non-modified-keys) |
| 418 | (princ ".") |
| 419 | (terpri)))) |
| 420 | (let* ((arglist (help-function-arglist def)) |
| 421 | (doc (documentation function)) |
| 422 | (usage (help-split-fundoc doc function))) |
| 423 | (with-current-buffer standard-output |
| 424 | ;; If definition is a keymap, skip arglist note. |
| 425 | (unless (keymapp def) |
| 426 | (let* ((use (cond |
| 427 | (usage (setq doc (cdr usage)) (car usage)) |
| 428 | ((listp arglist) |
| 429 | (format "%S" (help-make-usage function arglist))) |
| 430 | ((stringp arglist) arglist) |
| 431 | ;; Maybe the arglist is in the docstring of the alias. |
| 432 | ((let ((fun function)) |
| 433 | (while (and (symbolp fun) |
| 434 | (setq fun (symbol-function fun)) |
| 435 | (not (setq usage (help-split-fundoc |
| 436 | (documentation fun) |
| 437 | function))))) |
| 438 | usage) |
| 439 | (car usage)) |
| 440 | ((or (stringp def) |
| 441 | (vectorp def)) |
| 442 | (format "\nMacro: %s" (format-kbd-macro def))) |
| 443 | (t "[Missing arglist. Please make a bug report.]"))) |
| 444 | (high (help-highlight-arguments use doc))) |
| 445 | (insert (car high) "\n") |
| 446 | (setq doc (cdr high)))) |
| 447 | (let ((obsolete (and |
| 448 | ;; function might be a lambda construct. |
| 449 | (symbolp function) |
| 450 | (get function 'byte-obsolete-info)))) |
| 451 | (when obsolete |
| 452 | (princ "\nThis function is obsolete") |
| 453 | (when (nth 2 obsolete) |
| 454 | (insert (format " since %s" (nth 2 obsolete)))) |
| 455 | (insert ";\n" |
| 456 | (if (stringp (car obsolete)) (car obsolete) |
| 457 | (format "use `%s' instead." (car obsolete))) |
| 458 | "\n")) |
| 459 | (insert "\n" |
| 460 | (or doc "Not documented."))))))) |
| 461 | |
| 462 | \f |
| 463 | ;; Variables |
| 464 | |
| 465 | ;;;###autoload |
| 466 | (defun variable-at-point () |
| 467 | "Return the bound variable symbol found around point. |
| 468 | Return 0 if there is no such symbol." |
| 469 | (or (condition-case () |
| 470 | (with-syntax-table emacs-lisp-mode-syntax-table |
| 471 | (save-excursion |
| 472 | (or (not (zerop (skip-syntax-backward "_w"))) |
| 473 | (eq (char-syntax (following-char)) ?w) |
| 474 | (eq (char-syntax (following-char)) ?_) |
| 475 | (forward-sexp -1)) |
| 476 | (skip-chars-forward "'") |
| 477 | (let ((obj (read (current-buffer)))) |
| 478 | (and (symbolp obj) (boundp obj) obj)))) |
| 479 | (error nil)) |
| 480 | (let* ((str (find-tag-default)) |
| 481 | (obj (if str (intern str)))) |
| 482 | (and (symbolp obj) (boundp obj) obj)) |
| 483 | 0)) |
| 484 | |
| 485 | ;;;###autoload |
| 486 | (defun describe-variable (variable &optional buffer) |
| 487 | "Display the full documentation of VARIABLE (a symbol). |
| 488 | Returns the documentation as a string, also. |
| 489 | If VARIABLE has a buffer-local value in BUFFER (default to the current buffer), |
| 490 | it is displayed along with the global value." |
| 491 | (interactive |
| 492 | (let ((v (variable-at-point)) |
| 493 | (enable-recursive-minibuffers t) |
| 494 | val) |
| 495 | (setq val (completing-read (if (symbolp v) |
| 496 | (format |
| 497 | "Describe variable (default %s): " v) |
| 498 | "Describe variable: ") |
| 499 | obarray 'boundp t nil nil |
| 500 | (if (symbolp v) (symbol-name v)))) |
| 501 | (list (if (equal val "") |
| 502 | v (intern val))))) |
| 503 | (unless (buffer-live-p buffer) (setq buffer (current-buffer))) |
| 504 | (if (not (symbolp variable)) |
| 505 | (message "You did not specify a variable") |
| 506 | (save-excursion |
| 507 | (let* ((valvoid (not (with-current-buffer buffer (boundp variable)))) |
| 508 | ;; Extract the value before setting up the output buffer, |
| 509 | ;; in case `buffer' *is* the output buffer. |
| 510 | (val (unless valvoid (buffer-local-value variable buffer)))) |
| 511 | (help-setup-xref (list #'describe-variable variable buffer) |
| 512 | (interactive-p)) |
| 513 | (with-output-to-temp-buffer (help-buffer) |
| 514 | (with-current-buffer buffer |
| 515 | (prin1 variable) |
| 516 | (if valvoid |
| 517 | (princ " is void") |
| 518 | (with-current-buffer standard-output |
| 519 | (princ "'s value is ") |
| 520 | (terpri) |
| 521 | (let ((from (point))) |
| 522 | (pp val) |
| 523 | (help-xref-on-pp from (point)) |
| 524 | (if (< (point) (+ from 20)) |
| 525 | (delete-region (1- from) from))))) |
| 526 | (terpri) |
| 527 | (when (local-variable-p variable) |
| 528 | (princ (format "%socal in buffer %s; " |
| 529 | (if (get variable 'permanent-local) |
| 530 | "Permanently l" "L") |
| 531 | (buffer-name))) |
| 532 | (if (not (default-boundp variable)) |
| 533 | (princ "globally void") |
| 534 | (let ((val (default-value variable))) |
| 535 | (with-current-buffer standard-output |
| 536 | (princ "global value is ") |
| 537 | (terpri) |
| 538 | ;; Fixme: pp can take an age if you happen to |
| 539 | ;; ask for a very large expression. We should |
| 540 | ;; probably print it raw once and check it's a |
| 541 | ;; sensible size before prettyprinting. -- fx |
| 542 | (let ((from (point))) |
| 543 | (pp val) |
| 544 | (help-xref-on-pp from (point)) |
| 545 | (if (< (point) (+ from 20)) |
| 546 | (delete-region (1- from) from)))))) |
| 547 | (terpri)) |
| 548 | (terpri) |
| 549 | (with-current-buffer standard-output |
| 550 | (when (> (count-lines (point-min) (point-max)) 10) |
| 551 | ;; Note that setting the syntax table like below |
| 552 | ;; makes forward-sexp move over a `'s' at the end |
| 553 | ;; of a symbol. |
| 554 | (set-syntax-table emacs-lisp-mode-syntax-table) |
| 555 | (goto-char (point-min)) |
| 556 | (if valvoid |
| 557 | (forward-line 1) |
| 558 | (forward-sexp 1) |
| 559 | (delete-region (point) (progn (end-of-line) (point))) |
| 560 | (save-excursion |
| 561 | (insert "\n\nValue:") |
| 562 | (set (make-local-variable 'help-button-cache) |
| 563 | (point-marker))) |
| 564 | (insert " value is shown ") |
| 565 | (insert-button "below" |
| 566 | 'action help-button-cache |
| 567 | 'help-echo "mouse-2, RET: show value") |
| 568 | (insert ".\n\n"))) |
| 569 | ;; Add a note for variables that have been make-var-buffer-local. |
| 570 | (when (and (local-variable-if-set-p variable) |
| 571 | (or (not (local-variable-p variable)) |
| 572 | (with-temp-buffer |
| 573 | (local-variable-if-set-p variable)))) |
| 574 | (save-excursion |
| 575 | (forward-line -1) |
| 576 | (insert "Automatically becomes buffer-local when set in any fashion.\n")))) |
| 577 | ;; Mention if it's an alias |
| 578 | (let* ((alias (condition-case nil |
| 579 | (indirect-variable variable) |
| 580 | (error variable))) |
| 581 | (obsolete (get variable 'byte-obsolete-variable)) |
| 582 | (doc (or (documentation-property variable 'variable-documentation) |
| 583 | (documentation-property alias 'variable-documentation)))) |
| 584 | (unless (eq alias variable) |
| 585 | (princ (format "This variable is an alias for `%s'." alias)) |
| 586 | (terpri) |
| 587 | (terpri)) |
| 588 | (when obsolete |
| 589 | (princ "This variable is obsolete") |
| 590 | (if (cdr obsolete) (princ (format " since %s" (cdr obsolete)))) |
| 591 | (princ ";") (terpri) |
| 592 | (princ (if (stringp (car obsolete)) (car obsolete) |
| 593 | (format "use `%s' instead." (car obsolete)))) |
| 594 | (terpri) |
| 595 | (terpri)) |
| 596 | (princ (or doc "Not documented as a variable."))) |
| 597 | ;; Make a link to customize if this variable can be customized. |
| 598 | (if (custom-variable-p variable) |
| 599 | (let ((customize-label "customize")) |
| 600 | (terpri) |
| 601 | (terpri) |
| 602 | (princ (concat "You can " customize-label " this variable.")) |
| 603 | (with-current-buffer standard-output |
| 604 | (save-excursion |
| 605 | (re-search-backward |
| 606 | (concat "\\(" customize-label "\\)") nil t) |
| 607 | (help-xref-button 1 'help-customize-variable variable))))) |
| 608 | ;; Make a hyperlink to the library if appropriate. (Don't |
| 609 | ;; change the format of the buffer's initial line in case |
| 610 | ;; anything expects the current format.) |
| 611 | (let ((file-name (symbol-file (cons 'defvar variable)))) |
| 612 | (when (equal file-name "loaddefs.el") |
| 613 | ;; Find the real def site of the preloaded variable. |
| 614 | (let ((location |
| 615 | (condition-case nil |
| 616 | (find-variable-noselect variable file-name) |
| 617 | (error nil)))) |
| 618 | (when location |
| 619 | (with-current-buffer (car location) |
| 620 | (goto-char (cdr location)) |
| 621 | (when (re-search-backward |
| 622 | "^;;; Generated autoloads from \\(.*\\)" nil t) |
| 623 | (setq file-name (match-string 1))))))) |
| 624 | (when (and (null file-name) |
| 625 | (integerp (get variable 'variable-documentation))) |
| 626 | ;; It's a variable not defined in Elisp but in C. |
| 627 | (setq file-name |
| 628 | (if (get-buffer " *DOC*") |
| 629 | (help-C-file-name variable 'var) |
| 630 | 'C-source))) |
| 631 | (when file-name |
| 632 | (princ "\n\nDefined in `") |
| 633 | (princ (if (eq file-name 'C-source) "C source code" file-name)) |
| 634 | (princ "'.") |
| 635 | (with-current-buffer standard-output |
| 636 | (save-excursion |
| 637 | (re-search-backward "`\\([^`']+\\)'" nil t) |
| 638 | (help-xref-button 1 'help-variable-def |
| 639 | variable file-name))))) |
| 640 | |
| 641 | (print-help-return-message) |
| 642 | (save-excursion |
| 643 | (set-buffer standard-output) |
| 644 | ;; Return the text we displayed. |
| 645 | (buffer-string)))))))) |
| 646 | |
| 647 | |
| 648 | ;;;###autoload |
| 649 | (defun describe-syntax (&optional buffer) |
| 650 | "Describe the syntax specifications in the syntax table of BUFFER. |
| 651 | The descriptions are inserted in a help buffer, which is then displayed. |
| 652 | BUFFER defaults to the current buffer." |
| 653 | (interactive) |
| 654 | (setq buffer (or buffer (current-buffer))) |
| 655 | (help-setup-xref (list #'describe-syntax buffer) (interactive-p)) |
| 656 | (with-output-to-temp-buffer (help-buffer) |
| 657 | (let ((table (with-current-buffer buffer (syntax-table)))) |
| 658 | (with-current-buffer standard-output |
| 659 | (describe-vector table 'internal-describe-syntax-value) |
| 660 | (while (setq table (char-table-parent table)) |
| 661 | (insert "\nThe parent syntax table is:") |
| 662 | (describe-vector table 'internal-describe-syntax-value)))))) |
| 663 | |
| 664 | (defun help-describe-category-set (value) |
| 665 | (insert (cond |
| 666 | ((null value) "default") |
| 667 | ((char-table-p value) "deeper char-table ...") |
| 668 | (t (condition-case err |
| 669 | (category-set-mnemonics value) |
| 670 | (error "invalid")))))) |
| 671 | |
| 672 | ;;;###autoload |
| 673 | (defun describe-categories (&optional buffer) |
| 674 | "Describe the category specifications in the current category table. |
| 675 | The descriptions are inserted in a buffer, which is then displayed. |
| 676 | If BUFFER is non-nil, then describe BUFFER's category table instead. |
| 677 | BUFFER should be a buffer or a buffer name." |
| 678 | (interactive) |
| 679 | (setq buffer (or buffer (current-buffer))) |
| 680 | (help-setup-xref (list #'describe-categories buffer) (interactive-p)) |
| 681 | (with-output-to-temp-buffer (help-buffer) |
| 682 | (let ((table (with-current-buffer buffer (category-table)))) |
| 683 | (with-current-buffer standard-output |
| 684 | (describe-vector table 'help-describe-category-set) |
| 685 | (let ((docs (char-table-extra-slot table 0))) |
| 686 | (if (or (not (vectorp docs)) (/= (length docs) 95)) |
| 687 | (insert "Invalid first extra slot in this char table\n") |
| 688 | (insert "Meanings of mnemonic characters are:\n") |
| 689 | (dotimes (i 95) |
| 690 | (let ((elt (aref docs i))) |
| 691 | (when elt |
| 692 | (insert (+ i ?\ ) ": " elt "\n")))) |
| 693 | (while (setq table (char-table-parent table)) |
| 694 | (insert "\nThe parent category table is:") |
| 695 | (describe-vector table 'help-describe-category-set)))))))) |
| 696 | |
| 697 | (provide 'help-fns) |
| 698 | |
| 699 | ;;; arch-tag: 9e10331c-ae81-4d13-965d-c4819aaab0b3 |
| 700 | ;;; help-fns.el ends here |