help-fns.el 17.3 KB
Newer Older
1
;;; help-fns.el --- Complex help functions
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

;; Copyright (C) 1985, 1986, 1993, 1994, 1998, 1999, 2000, 2001
;;   Free Software Foundation, Inc.

;; Maintainer: FSF
;; Keywords: help, internal

;; This file is part of GNU Emacs.

;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;;; Commentary:

;; This file contains those help commands which are complicated, and
;; which may not be used in every session.  For example
;; `describe-function' will probably be heavily used when doing elisp
;; programming, but not if just editing C files.  Simpler help commands
;; are in help.el

;;; Code:

36 37 38
(require 'help-mode)


39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
;;;###autoload
(defun help-with-tutorial (&optional arg)
  "Select the Emacs learn-by-doing tutorial.
If there is a tutorial version written in the language
of the selected language environment, that version is used.
If there's no tutorial in that language, `TUTORIAL' is selected.
With arg, you are asked to choose which language."
  (interactive "P")
  (let ((lang (if arg
		  (read-language-name 'tutorial "Language: " "English")
		(if (get-language-info current-language-environment 'tutorial)
		    current-language-environment
		  "English")))
	file filename)
    (setq filename (get-language-info lang 'tutorial))
    (setq file (expand-file-name (concat "~/" filename)))
    (delete-other-windows)
    (if (get-file-buffer file)
	(switch-to-buffer (get-file-buffer file))
      (switch-to-buffer (create-file-buffer file))
      (setq buffer-file-name file)
      (setq default-directory (expand-file-name "~/"))
      (setq buffer-auto-save-file-name nil)
      (insert-file-contents (expand-file-name filename data-directory))
      (goto-char (point-min))
      (search-forward "\n<<")
      (beginning-of-line)
      (delete-region (point) (progn (end-of-line) (point)))
      (let ((n (- (window-height (selected-window))
		  (count-lines (point-min) (point))
		  6)))
	(if (< n 12)
	    (newline n)
	  ;; Some people get confused by the large gap.
	  (newline (/ n 2))
	  (insert "[Middle of page left blank for didactic purposes.  "
		  "Text continues below]")
	  (newline (- n (/ n 2)))))
      (goto-char (point-min))
      (set-buffer-modified-p nil))))

;;;###autoload
(defun locate-library (library &optional nosuffix path interactive-call)
  "Show the precise file name of Emacs library LIBRARY.
This command searches the directories in `load-path' like `M-x load-library'
to find the file that `M-x load-library RET LIBRARY RET' would load.
85
Optional second arg NOSUFFIX non-nil means don't add suffixes `load-suffixes'
86 87 88 89 90 91 92 93 94 95 96
to the specified name LIBRARY.

If the optional third arg PATH is specified, that list of directories
is used instead of `load-path'.

When called from a program, the file name is normaly returned as a
string.  When run interactively, the argument INTERACTIVE-CALL is t,
and the file name is displayed in the echo area."
  (interactive (list (read-string "Locate library: ")
		     nil nil
		     t))
97 98 99 100 101 102 103 104 105 106 107 108 109
  (catch 'answer
    (dolist (dir (or path load-path))
      (dolist (suf (append (unless nosuffix load-suffixes) '("")))
	(let ((try (expand-file-name (concat library suf) dir)))
	  (and (file-readable-p try)
	       (null (file-directory-p try))
	       (progn
		 (if interactive-call
		     (message "Library is file %s" (abbreviate-file-name try)))
		 (throw 'answer try))))))
    (if interactive-call
	(message "No library %s in search path" library))
    nil))
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128


;; Functions

;;;###autoload
(defun describe-function (function)
  "Display the full documentation of FUNCTION (a symbol)."
  (interactive
   (let ((fn (function-called-at-point))
	 (enable-recursive-minibuffers t)
	 val)
     (setq val (completing-read (if fn
				    (format "Describe function (default %s): " fn)
				  "Describe function: ")
				obarray 'fboundp t nil nil (symbol-name fn)))
     (list (if (equal val "")
	       fn (intern val)))))
  (if (null function)
      (message "You didn't specify a function")
129
    (help-setup-xref (list #'describe-function function) (interactive-p))
130 131 132 133 134 135 136 137 138 139 140
    (save-excursion
      (with-output-to-temp-buffer (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)
	(print-help-return-message)
	(with-current-buffer standard-output
	  ;; Return the text we displayed.
	  (buffer-string))))))
141

142 143
;;;###autoload
(defun describe-function-1 (function)
144 145 146
  (let* ((def (if (symbolp function)
		  (symbol-function function)
		function))
147
	 file-name string
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
	 (beg (if (commandp def) "an interactive " "a ")))
    (setq string
	  (cond ((or (stringp def)
		     (vectorp def))
		 "a keyboard macro")
		((subrp def)
		 (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"))
		((symbolp def)
		 (while (symbolp (symbol-function def))
		   (setq def (symbol-function def)))
		 (format "an alias for `%s'" def))
		((eq (car-safe def) 'lambda)
		 (concat beg "Lisp function"))
		((eq (car-safe def) 'macro)
		 "a Lisp macro")
		((eq (car-safe def) 'autoload)
		 (setq file-name (nth 1 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"))
			 ))
                ;; perhaps use keymapp here instead
                ((eq (car-safe def) 'keymap)
                 (let ((is-full nil)
                       (elts (cdr-safe def)))
                   (while elts
                     (if (char-table-p (car-safe elts))
                         (setq is-full t
                               elts nil))
                     (setq elts (cdr-safe elts)))
                   (if is-full
                       "a full keymap"
                     "a sparse keymap")))
		(t "")))
    (princ string)
188
    (with-current-buffer standard-output
189 190 191 192 193 194
      (save-excursion
	(save-match-data
	  (if (re-search-backward "alias for `\\([^`']+\\)'" nil t)
	      (help-xref-button 1 'help-function def)))))
    (or file-name
	(setq file-name (symbol-file function)))
195 196
    (cond
     (file-name
197 198 199 200 201 202
      (princ " in `")
      ;; We used to add .el to the file name,
      ;; but that's completely wrong when the user used load-file.
      (princ file-name)
      (princ "'")
      ;; Make a hyperlink to the library.
203
      (with-current-buffer standard-output
204 205
	(save-excursion
	  (re-search-backward "`\\([^`']+\\)'" nil t)
206
	  (help-xref-button 1 'help-function-def function file-name)))))
207 208 209
    (princ ".")
    (terpri)
    (when (commandp function)
210 211 212 213 214 215 216 217 218
      (let* ((binding (and (symbolp function) (commandp function)
			   (key-binding function nil t)))
	     (remapped (and (symbolp binding) (commandp binding) binding))
	     (keys (where-is-internal
		   (or remapped function) overriding-local-map nil nil)))
	(when remapped
	  (princ "It is remapped to `")
	  (princ (symbol-name remapped))
	  (princ "'"))
219
	(when keys
220
	  (princ (if remapped " which is bound to " "It is bound to "))
221
	  ;; FIXME: This list can be very long (f.ex. for self-insert-command).
222 223
	  (princ (mapconcat 'key-description keys ", ")))
	(when (or remapped keys)
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
	  (princ ".")
	  (terpri))))
    ;; Handle symbols aliased to other symbols.
    (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)))
    (let ((arglist (cond ((byte-code-function-p def)
			  (car (append def nil)))
			 ((eq (car-safe def) 'lambda)
			  (nth 1 def))
			 ((and (eq (car-safe def) 'autoload)
			       (not (eq (nth 4 def) 'keymap)))
			  (concat "[Arg list not available until "
				  "function definition is loaded.]"))
			 (t t))))
      (cond ((listp arglist)
	     (princ (cons (if (symbolp function) function "anonymous")
			  (mapcar (lambda (arg)
				    (if (memq arg '(&optional &rest))
					arg
				      (intern (upcase (symbol-name arg)))))
				  arglist)))
	     (terpri))
	    ((stringp arglist)
	     (princ arglist)
	     (terpri))))
    (let ((doc (documentation function)))
      (if doc
	  (progn (terpri)
		 (princ doc)
		 (if (subrp def)
		     (with-current-buffer standard-output
		       (beginning-of-line)
		       ;; Builtins get the calling sequence at the end of
		       ;; the doc string.  Move it to the same place as
		       ;; for other functions.

		       ;; In cases where `function' has been fset to a
		       ;; subr we can't search for function's name in
		       ;; the doc string.  Kluge round that using the
		       ;; printed representation.  The arg list then
		       ;; shows the wrong function name, but that
		       ;; might be a useful hint.
		       (let* ((rep (prin1-to-string def))
			      (name (progn
				      (string-match " \\([^ ]+\\)>$" rep)
				      (match-string 1 rep))))
			 (if (looking-at (format "(%s[ )]" (regexp-quote name)))
			     (let ((start (point-marker)))
			       (goto-char (point-min))
			       (forward-paragraph)
			       (insert-buffer-substring (current-buffer) start)
			       (insert ?\n)
			       (delete-region (1- start) (point-max)))
			   (goto-char (point-min))
			   (forward-paragraph)
			   (insert
			    "[Missing arglist.  Please make a bug report.]\n")))
283
		       (goto-char (point-max)))))
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
	(princ "not documented")))))


;; Variables

;;;###autoload
(defun variable-at-point ()
  "Return the bound variable symbol found around point.
Return 0 if there is no such symbol."
  (condition-case ()
      (with-syntax-table emacs-lisp-mode-syntax-table
	(save-excursion
	  (or (not (zerop (skip-syntax-backward "_w")))
	      (eq (char-syntax (following-char)) ?w)
	      (eq (char-syntax (following-char)) ?_)
	      (forward-sexp -1))
	  (skip-chars-forward "'")
	  (let ((obj (read (current-buffer))))
	    (or (and (symbolp obj) (boundp obj) obj)
		0))))
    (error 0)))

;;;###autoload
(defun describe-variable (variable &optional buffer)
  "Display the full documentation of VARIABLE (a symbol).
Returns the documentation as a string, also.
If VARIABLE has a buffer-local value in BUFFER (default to the current buffer),
it is displayed along with the global value."
  (interactive
   (let ((v (variable-at-point))
	 (enable-recursive-minibuffers t)
	 val)
     (setq val (completing-read (if (symbolp v)
				    (format
				     "Describe variable (default %s): " v)
				  "Describe variable: ")
				obarray 'boundp t nil nil
				(if (symbolp v) (symbol-name v))))
     (list (if (equal val "")
	       v (intern val)))))
  (unless (bufferp buffer) (setq buffer (current-buffer)))
  (if (not (symbolp variable))
      (message "You did not specify a variable")
327 328 329 330 331 332 333 334 335 336 337 338
    (save-excursion
      (let (valvoid)
	(help-setup-xref (list #'describe-variable variable buffer)
			 (interactive-p))
	(with-output-to-temp-buffer (help-buffer)
	  (with-current-buffer buffer
	    (prin1 variable)
	    (if (not (boundp variable))
		(progn
		  (princ " is void")
		  (setq valvoid t))
	      (let ((val (symbol-value variable)))
339
		(with-current-buffer standard-output
340
		  (princ "'s value is ")
341 342
		  (terpri)
		  (let ((from (point)))
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
		    (pp val)
		    (help-xref-on-pp from (point))
		    (if (< (point) (+ from 20))
			(save-excursion
			  (goto-char from)
			  (delete-char -1)))))))
	    (terpri)
	    (when (local-variable-p variable)
	      (princ (format "Local in buffer %s; " (buffer-name)))
	      (if (not (default-boundp variable))
		  (princ "globally void")
		(let ((val (default-value variable)))
		  (with-current-buffer standard-output
		    (princ "global value is ")
		    (terpri)
		    ;; Fixme: pp can take an age if you happen to
		    ;; ask for a very large expression.  We should
		    ;; probably print it raw once and check it's a
		    ;; sensible size before prettyprinting.  -- fx
		    (let ((from (point)))
		      (pp val)
		      (help-xref-on-pp from (point))
		      (if (< (point) (+ from 20))
			  (save-excursion
			    (goto-char from)
			    (delete-char -1)))))))
	      (terpri))
	    (terpri)
	    (with-current-buffer standard-output
	      (when (> (count-lines (point-min) (point-max)) 10)
		;; Note that setting the syntax table like below
		;; makes forward-sexp move over a `'s' at the end
		;; of a symbol.
		(set-syntax-table emacs-lisp-mode-syntax-table)
		(goto-char (point-min))
		(if valvoid
		    (forward-line 1)
		  (forward-sexp 1)
		  (delete-region (point) (progn (end-of-line) (point)))
		  (insert " value is shown below.\n\n")
		  (save-excursion
		    (insert "\n\nValue:"))))
	      ;; Add a note for variables that have been make-var-buffer-local.
	      (when (and (local-variable-if-set-p variable)
			 (or (not (local-variable-p variable))
			     (with-temp-buffer
			       (local-variable-if-set-p variable))))
390
		(save-excursion
391 392 393 394 395 396
		  (forward-line -1)
		  (insert "Automatically becomes buffer-local when set in any fashion.\n"))))
	    (princ "Documentation:")
	    (terpri)
	    (let ((doc (documentation-property variable 'variable-documentation)))
	      (princ (or doc "not documented as a variable.")))
397
	  
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
	    ;; Make a link to customize if this variable can be customized.
	    ;; Note, it is not reliable to test only for a custom-type property
	    ;; because those are only present after the var's definition
	    ;; has been loaded.
	    (if (or (get variable 'custom-type) ; after defcustom
		    (get variable 'custom-loads) ; from loaddefs.el
		    (get variable 'standard-value)) ; from cus-start.el
		(let ((customize-label "customize"))
		  (terpri)
		  (terpri)
		  (princ (concat "You can " customize-label " this variable."))
		  (with-current-buffer standard-output
		    (save-excursion
		      (re-search-backward
		       (concat "\\(" customize-label "\\)") nil t)
		      (help-xref-button 1 'help-customize-variable variable)))))
	    ;; Make a hyperlink to the library if appropriate.  (Don't
	    ;; change the format of the buffer's initial line in case
	    ;; anything expects the current format.)
	    (let ((file-name (symbol-file variable)))
	      (when (equal file-name "loaddefs.el")
		;; Find the real def site of the preloaded variable.
		(let ((location
		       (condition-case nil
			   (find-variable-noselect variable file-name)
			 (error nil))))
		  (when location
		    (with-current-buffer (car location)
		      (goto-char (cdr location))
		      (when (re-search-backward
			     "^;;; Generated autoloads from \\(.*\\)" nil t)
			(setq file-name (match-string 1)))))))
	      (when file-name
		(princ "\n\nDefined in `")
		(princ file-name)
		(princ "'.")
434 435
		(with-current-buffer standard-output
		  (save-excursion
436 437 438
		    (re-search-backward "`\\([^`']+\\)'" nil t)
		    (help-xref-button 1 'help-variable-def
				      variable file-name)))))
439

440 441 442 443 444
	    (print-help-return-message)
	    (save-excursion
	      (set-buffer standard-output)
	      ;; Return the text we displayed.
	      (buffer-string))))))))
445 446


Stefan Monnier's avatar
Stefan Monnier committed
447 448 449 450 451
;;;###autoload
(defun describe-syntax (&optional buffer)
  "Describe the syntax specifications in the syntax table of BUFFER.
The descriptions are inserted in a help buffer, which is then displayed.
BUFFER defaults to the current buffer."
452
  (interactive)
Stefan Monnier's avatar
Stefan Monnier committed
453 454 455 456 457 458 459 460 461 462
  (setq buffer (or buffer (current-buffer)))
  (help-setup-xref (list #'describe-syntax buffer) (interactive-p))
  (with-output-to-temp-buffer (help-buffer)
    (let ((table (with-current-buffer buffer (syntax-table))))
      (with-current-buffer standard-output
	(describe-vector table 'internal-describe-syntax-value)
	(while (setq table (char-table-parent table))
	  (insert "\nThe parent syntax table is:")
	  (describe-vector table 'internal-describe-syntax-value))))))

463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
(defun help-describe-category-set (value)
  (insert (cond
	   ((null value) "default")
	   ((char-table-p value) "deeper char-table ...")
	   (t (condition-case err
		  (category-set-mnemonics value)
		(error "invalid"))))))

;;;###autoload
(defun describe-categories (&optional buffer)
  "Describe the category specifications in the current category table.
The descriptions are inserted in a buffer, which is then displayed."
  (interactive)
  (setq buffer (or buffer (current-buffer)))
  (help-setup-xref (list #'describe-categories buffer) (interactive-p))
  (with-output-to-temp-buffer (help-buffer)
    (let ((table (with-current-buffer buffer (category-table))))
      (with-current-buffer standard-output
	(describe-vector table 'help-describe-category-set)
	(let ((docs (char-table-extra-slot table 0)))
	  (if (or (not (vectorp docs)) (/= (length docs) 95))
	      (insert "Invalid first extra slot in this char table\n")
	    (insert "Meanings of mnemonic characters are:\n")
	    (dotimes (i 95)
	      (let ((elt (aref docs i)))
		(when elt
		  (insert (+ i ?\ ) ": " elt "\n"))))
	    (while (setq table (char-table-parent table))
	      (insert "\nThe parent category table is:")
	      (describe-vector table 'help-describe-category-set))))))))

494
(provide 'help-fns)
495

496
;;; help-fns.el ends here