Commit | Line | Data |
---|---|---|
ed66e235 MR |
1 | ;;; help-fns.el --- Complex help functions\r |
2 | \r | |
3 | ;; Copyright (C) 1985, 1986, 1993, 1994, 1998, 1999, 2000, 2001,\r | |
4 | ;; 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.\r | |
5 | \r | |
6 | ;; Maintainer: FSF\r | |
7 | ;; Keywords: help, internal\r | |
8 | \r | |
9 | ;; This file is part of GNU Emacs.\r | |
10 | \r | |
11 | ;; GNU Emacs is free software: you can redistribute it and/or modify\r | |
12 | ;; it under the terms of the GNU General Public License as published by\r | |
13 | ;; the Free Software Foundation, either version 3 of the License, or\r | |
14 | ;; (at your option) any later version.\r | |
15 | \r | |
16 | ;; GNU Emacs is distributed in the hope that it will be useful,\r | |
17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of\r | |
18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r | |
19 | ;; GNU General Public License for more details.\r | |
20 | \r | |
21 | ;; You should have received a copy of the GNU General Public License\r | |
22 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.\r | |
23 | \r | |
24 | ;;; Commentary:\r | |
25 | \r | |
26 | ;; This file contains those help commands which are complicated, and\r | |
27 | ;; which may not be used in every session. For example\r | |
28 | ;; `describe-function' will probably be heavily used when doing elisp\r | |
29 | ;; programming, but not if just editing C files. Simpler help commands\r | |
30 | ;; are in help.el\r | |
31 | \r | |
32 | ;;; Code:\r | |
33 | \r | |
34 | (require 'help-mode)\r | |
35 | \r | |
36 | ;; Functions\r | |
37 | \r | |
38 | ;;;###autoload\r | |
39 | (defun describe-function (function)\r | |
40 | "Display the full documentation of FUNCTION (a symbol)."\r | |
41 | (interactive\r | |
42 | (let ((fn (function-called-at-point))\r | |
43 | (enable-recursive-minibuffers t)\r | |
44 | val)\r | |
45 | (setq val (completing-read (if fn\r | |
46 | (format "Describe function (default %s): " fn)\r | |
47 | "Describe function: ")\r | |
48 | obarray 'fboundp t nil nil\r | |
49 | (and fn (symbol-name fn))))\r | |
50 | (list (if (equal val "")\r | |
51 | fn (intern val)))))\r | |
52 | (if (null function)\r | |
53 | (message "You didn't specify a function")\r | |
54 | (help-setup-xref (list #'describe-function function) (interactive-p))\r | |
55 | (save-excursion\r | |
56 | (with-help-window (help-buffer)\r | |
57 | (prin1 function)\r | |
58 | ;; Use " is " instead of a colon so that\r | |
59 | ;; it is easier to get out the function name using forward-sexp.\r | |
60 | (princ " is ")\r | |
61 | (describe-function-1 function)\r | |
62 | (with-current-buffer standard-output\r | |
63 | ;; Return the text we displayed.\r | |
64 | (buffer-string))))))\r | |
65 | \r | |
66 | (defun help-split-fundoc (docstring def)\r | |
67 | "Split a function DOCSTRING into the actual doc and the usage info.\r | |
68 | Return (USAGE . DOC) or nil if there's no usage info.\r | |
69 | DEF is the function whose usage we're looking for in DOCSTRING."\r | |
70 | ;; Functions can get the calling sequence at the end of the doc string.\r | |
71 | ;; In cases where `function' has been fset to a subr we can't search for\r | |
72 | ;; function's name in the doc string so we use `fn' as the anonymous\r | |
73 | ;; function name instead.\r | |
74 | (when (and docstring (string-match "\n\n(fn\\(\\( .*\\)?)\\)\\'" docstring))\r | |
75 | (cons (format "(%s%s"\r | |
76 | ;; Replace `fn' with the actual function name.\r | |
77 | (if (consp def) "anonymous" def)\r | |
78 | (match-string 1 docstring))\r | |
79 | (substring docstring 0 (match-beginning 0)))))\r | |
80 | \r | |
81 | (defun help-add-fundoc-usage (docstring arglist)\r | |
82 | "Add the usage info to DOCSTRING.\r | |
83 | If DOCSTRING already has a usage info, then just return it unchanged.\r | |
84 | The usage info is built from ARGLIST. DOCSTRING can be nil.\r | |
85 | ARGLIST can also be t or a string of the form \"(FUN ARG1 ARG2 ...)\"."\r | |
86 | (unless (stringp docstring) (setq docstring "Not documented"))\r | |
87 | (if (or (string-match "\n\n(fn\\(\\( .*\\)?)\\)\\'" docstring) (eq arglist t))\r | |
88 | docstring\r | |
89 | (concat docstring\r | |
90 | (if (string-match "\n?\n\\'" docstring)\r | |
91 | (if (< (- (match-end 0) (match-beginning 0)) 2) "\n" "")\r | |
92 | "\n\n")\r | |
93 | (if (and (stringp arglist)\r | |
94 | (string-match "\\`([^ ]+\\(.*\\))\\'" arglist))\r | |
95 | (concat "(fn" (match-string 1 arglist) ")")\r | |
96 | (format "%S" (help-make-usage 'fn arglist))))))\r | |
97 | \r | |
98 | (defun help-function-arglist (def)\r | |
99 | ;; Handle symbols aliased to other symbols.\r | |
100 | (if (and (symbolp def) (fboundp def)) (setq def (indirect-function def)))\r | |
101 | ;; If definition is a macro, find the function inside it.\r | |
102 | (if (eq (car-safe def) 'macro) (setq def (cdr def)))\r | |
103 | (cond\r | |
104 | ((byte-code-function-p def) (aref def 0))\r | |
105 | ((eq (car-safe def) 'lambda) (nth 1 def))\r | |
106 | ((and (eq (car-safe def) 'autoload) (not (eq (nth 4 def) 'keymap)))\r | |
107 | "[Arg list not available until function definition is loaded.]")\r | |
108 | (t t)))\r | |
109 | \r | |
110 | (defun help-make-usage (function arglist)\r | |
111 | (cons (if (symbolp function) function 'anonymous)\r | |
112 | (mapcar (lambda (arg)\r | |
113 | (if (not (symbolp arg))\r | |
114 | (if (and (consp arg) (symbolp (car arg)))\r | |
115 | ;; CL style default values for optional args.\r | |
116 | (cons (intern (upcase (symbol-name (car arg))))\r | |
117 | (cdr arg))\r | |
118 | arg)\r | |
119 | (let ((name (symbol-name arg)))\r | |
120 | (if (string-match "\\`&" name) arg\r | |
121 | (intern (upcase name))))))\r | |
122 | arglist)))\r | |
123 | \r | |
124 | ;; Could be this, if we make symbol-file do the work below.\r | |
125 | ;; (defun help-C-file-name (subr-or-var kind)\r | |
126 | ;; "Return the name of the C file where SUBR-OR-VAR is defined.\r | |
127 | ;; KIND should be `var' for a variable or `subr' for a subroutine."\r | |
128 | ;; (symbol-file (if (symbolp subr-or-var) subr-or-var\r | |
129 | ;; (subr-name subr-or-var))\r | |
130 | ;; (if (eq kind 'var) 'defvar 'defun)))\r | |
131 | ;;;###autoload\r | |
132 | (defun help-C-file-name (subr-or-var kind)\r | |
133 | "Return the name of the C file where SUBR-OR-VAR is defined.\r | |
134 | KIND should be `var' for a variable or `subr' for a subroutine."\r | |
135 | (let ((docbuf (get-buffer-create " *DOC*"))\r | |
136 | (name (if (eq 'var kind)\r | |
137 | (concat "V" (symbol-name subr-or-var))\r | |
138 | (concat "F" (subr-name subr-or-var)))))\r | |
139 | (with-current-buffer docbuf\r | |
140 | (goto-char (point-min))\r | |
141 | (if (eobp)\r | |
142 | (insert-file-contents-literally\r | |
143 | (expand-file-name internal-doc-file-name doc-directory)))\r | |
144 | (let ((file (catch 'loop\r | |
145 | (while t\r | |
146 | (let ((pnt (search-forward (concat "\1f" name "\n"))))\r | |
147 | (re-search-backward "\1fS\\(.*\\)")\r | |
148 | (let ((file (match-string 1)))\r | |
149 | (if (member file build-files)\r | |
150 | (throw 'loop file)\r | |
151 | (goto-char pnt))))))))\r | |
152 | (if (string-match "^ns.*\\(\\.o\\|obj\\)\\'" file)\r | |
153 | (setq file (replace-match ".m" t t file 1))\r | |
154 | (if (string-match "\\.\\(o\\|obj\\)\\'" file)\r | |
155 | (setq file (replace-match ".c" t t file))))\r | |
156 | (if (string-match "\\.\\(c\\|m\\)\\'" file)\r | |
157 | (concat "src/" file)\r | |
158 | file)))))\r | |
159 | \r | |
160 | (defface help-argument-name '((((supports :slant italic)) :inherit italic))\r | |
161 | "Face to highlight argument names in *Help* buffers."\r | |
162 | :group 'help)\r | |
163 | \r | |
164 | (defun help-default-arg-highlight (arg)\r | |
165 | "Default function to highlight arguments in *Help* buffers.\r | |
166 | It returns ARG in face `help-argument-name'; ARG is also\r | |
167 | downcased if it displays differently than the default\r | |
168 | face (according to `face-differs-from-default-p')."\r | |
169 | (propertize (if (face-differs-from-default-p 'help-argument-name)\r | |
170 | (downcase arg)\r | |
171 | arg)\r | |
172 | 'face 'help-argument-name))\r | |
173 | \r | |
174 | (defun help-do-arg-highlight (doc args)\r | |
175 | (with-syntax-table (make-syntax-table emacs-lisp-mode-syntax-table)\r | |
176 | (modify-syntax-entry ?\- "w")\r | |
177 | (dolist (arg args doc)\r | |
178 | (setq doc (replace-regexp-in-string\r | |
179 | ;; This is heuristic, but covers all common cases\r | |
180 | ;; except ARG1-ARG2\r | |
181 | (concat "\\<" ; beginning of word\r | |
182 | "\\(?:[a-z-]*-\\)?" ; for xxx-ARG\r | |
183 | "\\("\r | |
184 | (regexp-quote arg)\r | |
185 | "\\)"\r | |
186 | "\\(?:es\\|s\\|th\\)?" ; for ARGth, ARGs\r | |
187 | "\\(?:-[a-z0-9-]+\\)?" ; for ARG-xxx, ARG-n\r | |
188 | "\\(?:-[{([<`\"].*?\\)?"; for ARG-{x}, (x), <x>, [x], `x'\r | |
189 | "\\>") ; end of word\r | |
190 | (help-default-arg-highlight arg)\r | |
191 | doc t t 1)))))\r | |
192 | \r | |
193 | (defun help-highlight-arguments (usage doc &rest args)\r | |
194 | (when usage\r | |
195 | (with-temp-buffer\r | |
196 | (insert usage)\r | |
197 | (goto-char (point-min))\r | |
198 | (let ((case-fold-search nil)\r | |
199 | (next (not (or args (looking-at "\\["))))\r | |
200 | (opt nil))\r | |
201 | ;; Make a list of all arguments\r | |
202 | (skip-chars-forward "^ ")\r | |
203 | (while next\r | |
204 | (or opt (not (looking-at " &")) (setq opt t))\r | |
205 | (if (not (re-search-forward " \\([\\[(]*\\)\\([^] &)\.]+\\)" nil t))\r | |
206 | (setq next nil)\r | |
207 | (setq args (cons (match-string 2) args))\r | |
208 | (when (and opt (string= (match-string 1) "("))\r | |
209 | ;; A pesky CL-style optional argument with default value,\r | |
210 | ;; so let's skip over it\r | |
211 | (search-backward "(")\r | |
212 | (goto-char (scan-sexps (point) 1)))))\r | |
213 | ;; Highlight aguments in the USAGE string\r | |
214 | (setq usage (help-do-arg-highlight (buffer-string) args))\r | |
215 | ;; Highlight arguments in the DOC string\r | |
216 | (setq doc (and doc (help-do-arg-highlight doc args))))))\r | |
217 | ;; Return value is like the one from help-split-fundoc, but highlighted\r | |
218 | (cons usage doc))\r | |
219 | \r | |
220 | ;; The following function was compiled from the former functions\r | |
221 | ;; `describe-simplify-lib-file-name' and `find-source-lisp-file' with\r | |
222 | ;; some excerpts from `describe-function-1' and `describe-variable'.\r | |
223 | ;; The only additional twists provided are (1) locate the defining file\r | |
224 | ;; for autoloaded functions, and (2) give preference to files in the\r | |
225 | ;; "install directory" (directories found via `load-path') rather than\r | |
226 | ;; to files in the "compile directory" (directories found by searching\r | |
227 | ;; the loaddefs.el file). We autoload it because it's also used by\r | |
228 | ;; `describe-face' (instead of `describe-simplify-lib-file-name').\r | |
229 | \r | |
230 | ;;;###autoload\r | |
231 | (defun find-lisp-object-file-name (object type)\r | |
232 | "Guess the file that defined the Lisp object OBJECT, of type TYPE.\r | |
233 | OBJECT should be a symbol associated with a function, variable, or face;\r | |
234 | alternatively, it can be a function definition.\r | |
235 | If TYPE is `variable', search for a variable definition.\r | |
236 | If TYPE is `face', search for a face definition.\r | |
237 | If TYPE is the value returned by `symbol-function' for a function symbol,\r | |
238 | search for a function definition.\r | |
239 | \r | |
240 | The return value is the absolute name of a readable file where OBJECT is\r | |
241 | defined. If several such files exist, preference is given to a file\r | |
242 | found via `load-path'. The return value can also be `C-source', which\r | |
243 | means that OBJECT is a function or variable defined in C. If no\r | |
244 | suitable file is found, return nil."\r | |
245 | (let* ((autoloaded (eq (car-safe type) 'autoload))\r | |
246 | (file-name (or (and autoloaded (nth 1 type))\r | |
247 | (symbol-file\r | |
248 | object (if (memq type (list 'defvar 'defface))\r | |
249 | type\r | |
250 | 'defun)))))\r | |
251 | (cond\r | |
252 | (autoloaded\r | |
253 | ;; An autoloaded function: Locate the file since `symbol-function'\r | |
254 | ;; has only returned a bare string here.\r | |
255 | (setq file-name\r | |
256 | (locate-file file-name load-path '(".el" ".elc") 'readable)))\r | |
257 | ((and (stringp file-name)\r | |
258 | (string-match "[.]*loaddefs.el\\'" file-name))\r | |
259 | ;; An autoloaded variable or face. Visit loaddefs.el in a buffer\r | |
260 | ;; and try to extract the defining file. The following form is\r | |
261 | ;; from `describe-function-1' and `describe-variable'.\r | |
262 | (let ((location\r | |
263 | (condition-case nil\r | |
264 | (find-function-search-for-symbol object nil file-name)\r | |
265 | (error nil))))\r | |
266 | (when location\r | |
267 | (with-current-buffer (car location)\r | |
268 | (goto-char (cdr location))\r | |
269 | (when (re-search-backward\r | |
270 | "^;;; Generated autoloads from \\(.*\\)" nil t)\r | |
271 | (setq file-name\r | |
272 | (locate-file\r | |
273 | (match-string-no-properties 1)\r | |
274 | load-path nil 'readable))))))))\r | |
275 | \r | |
276 | (cond\r | |
277 | ((and (not file-name) (subrp type))\r | |
278 | ;; A built-in function. The form is from `describe-function-1'.\r | |
279 | (if (get-buffer " *DOC*")\r | |
280 | (help-C-file-name type 'subr)\r | |
281 | 'C-source))\r | |
282 | ((and (not file-name) (symbolp object)\r | |
283 | (integerp (get object 'variable-documentation)))\r | |
284 | ;; A variable defined in C. The form is from `describe-variable'.\r | |
285 | (if (get-buffer " *DOC*")\r | |
286 | (help-C-file-name object 'var)\r | |
287 | 'C-source))\r | |
288 | ((not (stringp file-name))\r | |
289 | ;; If we don't have a file-name string by now, we lost.\r | |
290 | nil)\r | |
291 | ((let ((lib-name\r | |
292 | (if (string-match "[.]elc\\'" file-name)\r | |
293 | (substring-no-properties file-name 0 -1)\r | |
294 | file-name)))\r | |
295 | ;; When the Elisp source file can be found in the install\r | |
296 | ;; directory return the name of that file - `file-name' should\r | |
297 | ;; have become an absolute file name ny now.\r | |
298 | (or (and (file-readable-p lib-name) lib-name)\r | |
299 | ;; The library might be compressed.\r | |
300 | (and (file-readable-p (concat lib-name ".gz")) lib-name))))\r | |
301 | ((let* ((lib-name (file-name-nondirectory file-name))\r | |
302 | ;; The next form is from `describe-simplify-lib-file-name'.\r | |
303 | (file-name\r | |
304 | ;; Try converting the absolute file name to a library\r | |
305 | ;; name, convert that back to a file name and see if we\r | |
306 | ;; get the original one. If so, they are equivalent.\r | |
307 | (if (equal file-name (locate-file lib-name load-path '("")))\r | |
308 | (if (string-match "[.]elc\\'" lib-name)\r | |
309 | (substring-no-properties lib-name 0 -1)\r | |
310 | lib-name)\r | |
311 | file-name))\r | |
312 | ;; The next three forms are from `find-source-lisp-file'.\r | |
313 | (elc-file (locate-file\r | |
314 | (concat file-name\r | |
315 | (if (string-match "\\.el\\'" file-name)\r | |
316 | "c"\r | |
317 | ".elc"))\r | |
318 | load-path nil 'readable))\r | |
319 | (str (when elc-file\r | |
320 | (with-temp-buffer\r | |
321 | (insert-file-contents-literally elc-file nil 0 256)\r | |
322 | (buffer-string))))\r | |
323 | (src-file (and str\r | |
324 | (string-match ";;; from file \\(.*\\.el\\)" str)\r | |
325 | (match-string 1 str))))\r | |
326 | (and src-file (file-readable-p src-file) src-file))))))\r | |
327 | \r | |
328 | (declare-function ad-get-advice-info "advice" (function))\r | |
329 | \r | |
330 | ;;;###autoload\r | |
331 | (defun describe-function-1 (function)\r | |
332 | (let* ((advised (and (symbolp function) (featurep 'advice)\r | |
333 | (ad-get-advice-info function)))\r | |
334 | ;; If the function is advised, use the symbol that has the\r | |
335 | ;; real definition, if that symbol is already set up.\r | |
336 | (real-function\r | |
337 | (or (and advised\r | |
338 | (let ((origname (cdr (assq 'origname advised))))\r | |
339 | (and (fboundp origname) origname)))\r | |
340 | function))\r | |
341 | ;; Get the real definition.\r | |
342 | (def (if (symbolp real-function)\r | |
343 | (symbol-function real-function)\r | |
344 | function))\r | |
345 | file-name string\r | |
346 | (beg (if (commandp def) "an interactive " "a "))\r | |
347 | (pt1 (with-current-buffer (help-buffer) (point)))\r | |
348 | errtype)\r | |
349 | (setq string\r | |
350 | (cond ((or (stringp def)\r | |
351 | (vectorp def))\r | |
352 | "a keyboard macro")\r | |
353 | ((subrp def)\r | |
354 | (if (eq 'unevalled (cdr (subr-arity def)))\r | |
355 | (concat beg "special form")\r | |
356 | (concat beg "built-in function")))\r | |
357 | ((byte-code-function-p def)\r | |
358 | (concat beg "compiled Lisp function"))\r | |
359 | ((symbolp def)\r | |
360 | (while (and (fboundp def)\r | |
361 | (symbolp (symbol-function def)))\r | |
362 | (setq def (symbol-function def)))\r | |
363 | ;; Handle (defalias 'foo 'bar), where bar is undefined.\r | |
364 | (or (fboundp def) (setq errtype 'alias))\r | |
365 | (format "an alias for `%s'" def))\r | |
366 | ((eq (car-safe def) 'lambda)\r | |
367 | (concat beg "Lisp function"))\r | |
368 | ((eq (car-safe def) 'macro)\r | |
369 | "a Lisp macro")\r | |
370 | ((eq (car-safe def) 'autoload)\r | |
371 | (format "%s autoloaded %s"\r | |
372 | (if (commandp def) "an interactive" "an")\r | |
373 | (if (eq (nth 4 def) 'keymap) "keymap"\r | |
374 | (if (nth 4 def) "Lisp macro" "Lisp function"))))\r | |
375 | ((keymapp def)\r | |
376 | (let ((is-full nil)\r | |
377 | (elts (cdr-safe def)))\r | |
378 | (while elts\r | |
379 | (if (char-table-p (car-safe elts))\r | |
380 | (setq is-full t\r | |
381 | elts nil))\r | |
382 | (setq elts (cdr-safe elts)))\r | |
383 | (if is-full\r | |
384 | "a full keymap"\r | |
385 | "a sparse keymap")))\r | |
386 | (t "")))\r | |
387 | (princ string)\r | |
388 | (if (eq errtype 'alias)\r | |
389 | (princ ",\nwhich is not defined. Please make a bug report.")\r | |
390 | (with-current-buffer standard-output\r | |
391 | (save-excursion\r | |
392 | (save-match-data\r | |
393 | (when (re-search-backward "alias for `\\([^`']+\\)'" nil t)\r | |
394 | (help-xref-button 1 'help-function def)))))\r | |
395 | \r | |
396 | (setq file-name (find-lisp-object-file-name function def))\r | |
397 | (when file-name\r | |
398 | (princ " in `")\r | |
399 | ;; We used to add .el to the file name,\r | |
400 | ;; but that's completely wrong when the user used load-file.\r | |
401 | (princ (if (eq file-name 'C-source) "C source code" file-name))\r | |
402 | (princ "'")\r | |
403 | ;; Make a hyperlink to the library.\r | |
404 | (with-current-buffer standard-output\r | |
405 | (save-excursion\r | |
406 | (re-search-backward "`\\([^`']+\\)'" nil t)\r | |
407 | (help-xref-button 1 'help-function-def real-function file-name))))\r | |
408 | (princ ".")\r | |
409 | (with-current-buffer (help-buffer)\r | |
410 | (fill-region-as-paragraph (save-excursion (goto-char pt1) (forward-line 0) (point))\r | |
411 | (point)))\r | |
412 | (terpri)(terpri)\r | |
413 | (when (commandp function)\r | |
414 | (let ((pt2 (with-current-buffer (help-buffer) (point))))\r | |
415 | (if (and (eq function 'self-insert-command)\r | |
416 | (eq (key-binding "a") 'self-insert-command)\r | |
417 | (eq (key-binding "b") 'self-insert-command)\r | |
418 | (eq (key-binding "c") 'self-insert-command))\r | |
419 | (princ "It is bound to many ordinary text characters.\n")\r | |
420 | (let* ((remapped (command-remapping function))\r | |
421 | (keys (where-is-internal\r | |
422 | (or remapped function) overriding-local-map nil nil))\r | |
423 | non-modified-keys)\r | |
424 | ;; Which non-control non-meta keys run this command?\r | |
425 | (dolist (key keys)\r | |
426 | (if (member (event-modifiers (aref key 0)) '(nil (shift)))\r | |
427 | (push key non-modified-keys)))\r | |
428 | (when remapped\r | |
429 | (princ "It is remapped to `")\r | |
430 | (princ (symbol-name remapped))\r | |
431 | (princ "'"))\r | |
432 | \r | |
433 | (when keys\r | |
434 | (princ (if remapped ", which is bound to " "It is bound to "))\r | |
435 | ;; If lots of ordinary text characters run this command,\r | |
436 | ;; don't mention them one by one.\r | |
437 | (if (< (length non-modified-keys) 10)\r | |
438 | (princ (mapconcat 'key-description keys ", "))\r | |
439 | (dolist (key non-modified-keys)\r | |
440 | (setq keys (delq key keys)))\r | |
441 | (if keys\r | |
442 | (progn\r | |
443 | (princ (mapconcat 'key-description keys ", "))\r | |
444 | (princ ", and many ordinary text characters"))\r | |
445 | (princ "many ordinary text characters"))))\r | |
446 | (when (or remapped keys non-modified-keys)\r | |
447 | (princ ".")\r | |
448 | (terpri))))\r | |
449 | (with-current-buffer (help-buffer) (fill-region-as-paragraph pt2 (point)))\r | |
450 | (terpri)))\r | |
451 | (let* ((arglist (help-function-arglist def))\r | |
452 | (doc (documentation function))\r | |
453 | (usage (help-split-fundoc doc function)))\r | |
454 | (with-current-buffer standard-output\r | |
455 | ;; If definition is a keymap, skip arglist note.\r | |
456 | (unless (keymapp function)\r | |
457 | (let* ((use (cond\r | |
458 | (usage (setq doc (cdr usage)) (car usage))\r | |
459 | ((listp arglist)\r | |
460 | (format "%S" (help-make-usage function arglist)))\r | |
461 | ((stringp arglist) arglist)\r | |
462 | ;; Maybe the arglist is in the docstring of a symbol\r | |
463 | ;; this one is aliased to.\r | |
464 | ((let ((fun real-function))\r | |
465 | (while (and (symbolp fun)\r | |
466 | (setq fun (symbol-function fun))\r | |
467 | (not (setq usage (help-split-fundoc\r | |
468 | (documentation fun)\r | |
469 | function)))))\r | |
470 | usage)\r | |
471 | (car usage))\r | |
472 | ((or (stringp def)\r | |
473 | (vectorp def))\r | |
474 | (format "\nMacro: %s" (format-kbd-macro def)))\r | |
475 | (t "[Missing arglist. Please make a bug report.]")))\r | |
476 | (high (help-highlight-arguments use doc)))\r | |
477 | (let ((fill-begin (point)))\r | |
478 | (insert (car high) "\n")\r | |
479 | (fill-region fill-begin (point)))\r | |
480 | (setq doc (cdr high))))\r | |
481 | (let* ((obsolete (and\r | |
482 | ;; function might be a lambda construct.\r | |
483 | (symbolp function)\r | |
484 | (get function 'byte-obsolete-info)))\r | |
485 | (use (car obsolete)))\r | |
486 | (when obsolete\r | |
487 | (princ "\nThis function is obsolete")\r | |
488 | (when (nth 2 obsolete)\r | |
489 | (insert (format " since %s" (nth 2 obsolete))))\r | |
490 | (insert (cond ((stringp use) (concat ";\n" use))\r | |
491 | (use (format ";\nuse `%s' instead." use))\r | |
492 | (t "."))\r | |
493 | "\n"))\r | |
494 | (insert "\n"\r | |
495 | (or doc "Not documented."))))))))\r | |
496 | \r | |
497 | \f\r | |
498 | ;; Variables\r | |
499 | \r | |
500 | ;;;###autoload\r | |
501 | (defun variable-at-point (&optional any-symbol)\r | |
502 | "Return the bound variable symbol found at or before point.\r | |
503 | Return 0 if there is no such symbol.\r | |
504 | If ANY-SYMBOL is non-nil, don't insist the symbol be bound."\r | |
505 | (with-syntax-table emacs-lisp-mode-syntax-table\r | |
506 | (or (condition-case ()\r | |
507 | (save-excursion\r | |
508 | (or (not (zerop (skip-syntax-backward "_w")))\r | |
509 | (eq (char-syntax (following-char)) ?w)\r | |
510 | (eq (char-syntax (following-char)) ?_)\r | |
511 | (forward-sexp -1))\r | |
512 | (skip-chars-forward "'")\r | |
513 | (let ((obj (read (current-buffer))))\r | |
514 | (and (symbolp obj) (boundp obj) obj)))\r | |
515 | (error nil))\r | |
516 | (let* ((str (find-tag-default))\r | |
517 | (sym (if str (intern-soft str))))\r | |
518 | (if (and sym (or any-symbol (boundp sym)))\r | |
519 | sym\r | |
520 | (save-match-data\r | |
521 | (when (and str (string-match "\\`\\W*\\(.*?\\)\\W*\\'" str))\r | |
522 | (setq sym (intern-soft (match-string 1 str)))\r | |
523 | (and (or any-symbol (boundp sym)) sym)))))\r | |
524 | 0)))\r | |
525 | \r | |
526 | (defun describe-variable-custom-version-info (variable)\r | |
527 | (let ((custom-version (get variable 'custom-version))\r | |
528 | (cpv (get variable 'custom-package-version))\r | |
529 | (output nil))\r | |
530 | (if custom-version\r | |
531 | (setq output\r | |
532 | (format "This variable was introduced, or its default value was changed, in\nversion %s of Emacs.\n"\r | |
533 | custom-version))\r | |
534 | (when cpv\r | |
535 | (let* ((package (car-safe cpv))\r | |
536 | (version (if (listp (cdr-safe cpv))\r | |
537 | (car (cdr-safe cpv))\r | |
538 | (cdr-safe cpv)))\r | |
539 | (pkg-versions (assq package customize-package-emacs-version-alist))\r | |
540 | (emacsv (cdr (assoc version pkg-versions))))\r | |
541 | (if (and package version)\r | |
542 | (setq output\r | |
543 | (format (concat "This variable was introduced, or its default value was changed, in\nversion %s of the %s package"\r | |
544 | (if emacsv\r | |
545 | (format " that is part of Emacs %s" emacsv))\r | |
546 | ".\n")\r | |
547 | version package))))))\r | |
548 | output))\r | |
549 | \r | |
550 | ;;;###autoload\r | |
551 | (defun describe-variable (variable &optional buffer frame)\r | |
552 | "Display the full documentation of VARIABLE (a symbol).\r | |
553 | Returns the documentation as a string, also.\r | |
554 | If VARIABLE has a buffer-local value in BUFFER or FRAME\r | |
555 | \(default to the current buffer and current frame),\r | |
556 | it is displayed along with the global value."\r | |
557 | (interactive\r | |
558 | (let ((v (variable-at-point))\r | |
559 | (enable-recursive-minibuffers t)\r | |
560 | val)\r | |
561 | (setq val (completing-read (if (symbolp v)\r | |
562 | (format\r | |
563 | "Describe variable (default %s): " v)\r | |
564 | "Describe variable: ")\r | |
565 | obarray\r | |
566 | '(lambda (vv)\r | |
567 | (or (boundp vv)\r | |
568 | (get vv 'variable-documentation)))\r | |
569 | t nil nil\r | |
570 | (if (symbolp v) (symbol-name v))))\r | |
571 | (list (if (equal val "")\r | |
572 | v (intern val)))))\r | |
573 | (let (file-name)\r | |
574 | (unless (buffer-live-p buffer) (setq buffer (current-buffer)))\r | |
575 | (unless (frame-live-p frame) (setq frame (selected-frame)))\r | |
576 | (if (not (symbolp variable))\r | |
577 | (message "You did not specify a variable")\r | |
578 | (save-excursion\r | |
579 | (let ((valvoid (not (with-current-buffer buffer (boundp variable))))\r | |
580 | val val-start-pos locus)\r | |
581 | ;; Extract the value before setting up the output buffer,\r | |
582 | ;; in case `buffer' *is* the output buffer.\r | |
583 | (unless valvoid\r | |
584 | (with-selected-frame frame\r | |
585 | (with-current-buffer buffer\r | |
586 | (setq val (symbol-value variable)\r | |
587 | locus (variable-binding-locus variable)))))\r | |
588 | (help-setup-xref (list #'describe-variable variable buffer)\r | |
589 | (interactive-p))\r | |
590 | (with-help-window (help-buffer)\r | |
591 | (with-current-buffer buffer\r | |
592 | (prin1 variable)\r | |
593 | (setq file-name (find-lisp-object-file-name variable 'defvar))\r | |
594 | \r | |
595 | (if file-name\r | |
596 | (progn\r | |
597 | (princ " is a variable defined in `")\r | |
598 | (princ (if (eq file-name 'C-source) "C source code" file-name))\r | |
599 | (princ "'.\n")\r | |
600 | (with-current-buffer standard-output\r | |
601 | (save-excursion\r | |
602 | (re-search-backward "`\\([^`']+\\)'" nil t)\r | |
603 | (help-xref-button 1 'help-variable-def\r | |
604 | variable file-name)))\r | |
605 | (if valvoid\r | |
606 | (princ "It is void as a variable.")\r | |
607 | (princ "Its ")))\r | |
608 | (if valvoid\r | |
609 | (princ " is void as a variable.")\r | |
610 | (princ "'s "))))\r | |
611 | (if valvoid\r | |
612 | nil\r | |
613 | (with-current-buffer standard-output\r | |
614 | (setq val-start-pos (point))\r | |
615 | (princ "value is ")\r | |
616 | (terpri)\r | |
617 | (let ((from (point)))\r | |
618 | (pp val)\r | |
619 | ;; Hyperlinks in variable's value are quite frequently\r | |
620 | ;; inappropriate e.g C-h v <RET> features <RET>\r | |
621 | ;; (help-xref-on-pp from (point))\r | |
622 | (if (< (point) (+ from 20))\r | |
623 | (delete-region (1- from) from)))))\r | |
624 | (terpri)\r | |
625 | \r | |
626 | (when locus\r | |
627 | (if (bufferp locus)\r | |
628 | (princ (format "%socal in buffer %s; "\r | |
629 | (if (get variable 'permanent-local)\r | |
630 | "Permanently l" "L")\r | |
631 | (buffer-name)))\r | |
632 | (princ (format "It is a frame-local variable; ")))\r | |
633 | (if (not (default-boundp variable))\r | |
634 | (princ "globally void")\r | |
635 | (let ((val (default-value variable)))\r | |
636 | (with-current-buffer standard-output\r | |
637 | (princ "global value is ")\r | |
638 | (terpri)\r | |
639 | ;; Fixme: pp can take an age if you happen to\r | |
640 | ;; ask for a very large expression. We should\r | |
641 | ;; probably print it raw once and check it's a\r | |
642 | ;; sensible size before prettyprinting. -- fx\r | |
643 | (let ((from (point)))\r | |
644 | (pp val)\r | |
645 | ;; See previous comment for this function.\r | |
646 | ;; (help-xref-on-pp from (point))\r | |
647 | (if (< (point) (+ from 20))\r | |
648 | (delete-region (1- from) from))))))\r | |
649 | (terpri))\r | |
650 | \r | |
651 | ;; If the value is large, move it to the end.\r | |
652 | (with-current-buffer standard-output\r | |
653 | (when (> (count-lines (point-min) (point-max)) 10)\r | |
654 | ;; Note that setting the syntax table like below\r | |
655 | ;; makes forward-sexp move over a `'s' at the end\r | |
656 | ;; of a symbol.\r | |
657 | (set-syntax-table emacs-lisp-mode-syntax-table)\r | |
658 | (goto-char val-start-pos)\r | |
659 | ;; The line below previously read as\r | |
660 | ;; (delete-region (point) (progn (end-of-line) (point)))\r | |
661 | ;; which suppressed display of the buffer local value for\r | |
662 | ;; large values.\r | |
663 | (when (looking-at "value is") (replace-match ""))\r | |
664 | (save-excursion\r | |
665 | (insert "\n\nValue:")\r | |
666 | (set (make-local-variable 'help-button-cache)\r | |
667 | (point-marker)))\r | |
668 | (insert "value is shown ")\r | |
669 | (insert-button "below"\r | |
670 | 'action help-button-cache\r | |
671 | 'follow-link t\r | |
672 | 'help-echo "mouse-2, RET: show value")\r | |
673 | (insert ".\n")))\r | |
674 | (terpri)\r | |
675 | \r | |
676 | (let* ((alias (condition-case nil\r | |
677 | (indirect-variable variable)\r | |
678 | (error variable)))\r | |
679 | (obsolete (get variable 'byte-obsolete-variable))\r | |
680 | (use (car obsolete))\r | |
681 | (safe-var (get variable 'safe-local-variable))\r | |
682 | (doc (or (documentation-property variable 'variable-documentation)\r | |
683 | (documentation-property alias 'variable-documentation)))\r | |
684 | (extra-line nil))\r | |
685 | ;; Add a note for variables that have been make-var-buffer-local.\r | |
686 | (when (and (local-variable-if-set-p variable)\r | |
687 | (or (not (local-variable-p variable))\r | |
688 | (with-temp-buffer\r | |
689 | (local-variable-if-set-p variable))))\r | |
690 | (setq extra-line t)\r | |
691 | (princ " Automatically becomes buffer-local when set in any fashion.\n"))\r | |
692 | \r | |
693 | ;; Mention if it's an alias\r | |
694 | (unless (eq alias variable)\r | |
695 | (setq extra-line t)\r | |
696 | (princ (format " This variable is an alias for `%s'.\n" alias)))\r | |
697 | \r | |
698 | (when obsolete\r | |
699 | (setq extra-line t)\r | |
700 | (princ " This variable is obsolete")\r | |
701 | (if (cdr obsolete) (princ (format " since %s" (cdr obsolete))))\r | |
702 | (princ (cond ((stringp use) (concat ";\n " use))\r | |
703 | (use (format ";\n use `%s' instead." (car obsolete)))\r | |
704 | (t ".")))\r | |
705 | (terpri))\r | |
706 | (when safe-var\r | |
707 | (setq extra-line t)\r | |
708 | (princ " This variable is safe as a file local variable ")\r | |
709 | (princ "if its value\n satisfies the predicate ")\r | |
710 | (princ (if (byte-code-function-p safe-var)\r | |
711 | "which is byte-compiled expression.\n"\r | |
712 | (format "`%s'.\n" safe-var))))\r | |
713 | \r | |
714 | (if extra-line (terpri))\r | |
715 | (princ "Documentation:\n")\r | |
716 | (with-current-buffer standard-output\r | |
717 | (insert (or doc "Not documented as a variable."))))\r | |
718 | \r | |
719 | ;; Make a link to customize if this variable can be customized.\r | |
720 | (when (custom-variable-p variable)\r | |
721 | (let ((customize-label "customize"))\r | |
722 | (terpri)\r | |
723 | (terpri)\r | |
724 | (princ (concat "You can " customize-label " this variable."))\r | |
725 | (with-current-buffer standard-output\r | |
726 | (save-excursion\r | |
727 | (re-search-backward\r | |
728 | (concat "\\(" customize-label "\\)") nil t)\r | |
729 | (help-xref-button 1 'help-customize-variable variable))))\r | |
730 | ;; Note variable's version or package version\r | |
731 | (let ((output (describe-variable-custom-version-info variable)))\r | |
732 | (when output\r | |
733 | (terpri)\r | |
734 | (terpri)\r | |
735 | (princ output))))\r | |
736 | \r | |
737 | (save-excursion\r | |
738 | (set-buffer standard-output)\r | |
739 | ;; Return the text we displayed.\r | |
740 | (buffer-string))))))))\r | |
741 | \r | |
742 | \r | |
743 | ;;;###autoload\r | |
744 | (defun describe-syntax (&optional buffer)\r | |
745 | "Describe the syntax specifications in the syntax table of BUFFER.\r | |
746 | The descriptions are inserted in a help buffer, which is then displayed.\r | |
747 | BUFFER defaults to the current buffer."\r | |
748 | (interactive)\r | |
749 | (setq buffer (or buffer (current-buffer)))\r | |
750 | (help-setup-xref (list #'describe-syntax buffer) (interactive-p))\r | |
751 | (with-help-window (help-buffer)\r | |
752 | (let ((table (with-current-buffer buffer (syntax-table))))\r | |
753 | (with-current-buffer standard-output\r | |
754 | (describe-vector table 'internal-describe-syntax-value)\r | |
755 | (while (setq table (char-table-parent table))\r | |
756 | (insert "\nThe parent syntax table is:")\r | |
757 | (describe-vector table 'internal-describe-syntax-value))))))\r | |
758 | \r | |
759 | (defun help-describe-category-set (value)\r | |
760 | (insert (cond\r | |
761 | ((null value) "default")\r | |
762 | ((char-table-p value) "deeper char-table ...")\r | |
763 | (t (condition-case err\r | |
764 | (category-set-mnemonics value)\r | |
765 | (error "invalid"))))))\r | |
766 | \r | |
767 | ;;;###autoload\r | |
768 | (defun describe-categories (&optional buffer)\r | |
769 | "Describe the category specifications in the current category table.\r | |
770 | The descriptions are inserted in a buffer, which is then displayed.\r | |
771 | If BUFFER is non-nil, then describe BUFFER's category table instead.\r | |
772 | BUFFER should be a buffer or a buffer name."\r | |
773 | (interactive)\r | |
774 | (setq buffer (or buffer (current-buffer)))\r | |
775 | (help-setup-xref (list #'describe-categories buffer) (interactive-p))\r | |
776 | (with-help-window (help-buffer)\r | |
777 | (let ((table (with-current-buffer buffer (category-table))))\r | |
778 | (with-current-buffer standard-output\r | |
779 | (describe-vector table 'help-describe-category-set)\r | |
780 | (let ((docs (char-table-extra-slot table 0)))\r | |
781 | (if (or (not (vectorp docs)) (/= (length docs) 95))\r | |
782 | (insert "Invalid first extra slot in this char table\n")\r | |
783 | (insert "Meanings of mnemonic characters are:\n")\r | |
784 | (dotimes (i 95)\r | |
785 | (let ((elt (aref docs i)))\r | |
786 | (when elt\r | |
787 | (insert (+ i ?\s) ": " elt "\n"))))\r | |
788 | (while (setq table (char-table-parent table))\r | |
789 | (insert "\nThe parent category table is:")\r | |
790 | (describe-vector table 'help-describe-category-set))))))))\r | |
791 | \r | |
792 | (provide 'help-fns)\r | |
793 | \r | |
794 | ;; arch-tag: 9e10331c-ae81-4d13-965d-c4819aaab0b3\r | |
795 | ;;; help-fns.el ends here\r |