Commit | Line | Data |
---|---|---|
1a06eabd ER |
1 | ;;; help.el --- help commands for Emacs |
2 | ||
754084bb GM |
3 | ;; Copyright (C) 1985, 1986, 1993, 1994, 1998, 1999, 2000, 2001 |
4 | ;; Free Software Foundation, Inc. | |
3a801d0c | 5 | |
e5167999 | 6 | ;; Maintainer: FSF |
fd7fa35a | 7 | ;; Keywords: help, internal |
e5167999 | 8 | |
433ae6f6 RS |
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 | |
e5167999 | 13 | ;; the Free Software Foundation; either version 2, or (at your option) |
433ae6f6 RS |
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 | |
b578f267 EN |
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. | |
433ae6f6 | 25 | |
d9ecc911 ER |
26 | ;;; Commentary: |
27 | ||
a1c9f209 | 28 | ;; This code implements GNU Emacs' on-line help system, the one invoked by |
95ac0a6f | 29 | ;; `M-x help-for-help'. |
d9ecc911 | 30 | |
e5167999 ER |
31 | ;;; Code: |
32 | ||
8aa3a187 RS |
33 | ;; Get the macro make-help-screen when this is compiled, |
34 | ;; or run interpreted, but not when the compiled code is loaded. | |
b1fe9304 | 35 | (eval-when-compile (require 'help-macro)) |
4a8adb0b | 36 | (eval-when-compile (require 'view)) |
41b8542b | 37 | |
433ae6f6 RS |
38 | (defvar help-map (make-sparse-keymap) |
39 | "Keymap for characters following the Help key.") | |
40 | ||
afaa65e4 KH |
41 | (defvar help-mode-map (make-sparse-keymap) |
42 | "Keymap for help mode.") | |
43 | ||
e17d2fd1 | 44 | (define-key global-map (char-to-string help-char) 'help-command) |
0af3df1c RS |
45 | (define-key global-map [help] 'help-command) |
46 | (define-key global-map [f1] 'help-command) | |
433ae6f6 RS |
47 | (fset 'help-command help-map) |
48 | ||
e17d2fd1 | 49 | (define-key help-map (char-to-string help-char) 'help-for-help) |
0af3df1c RS |
50 | (define-key help-map [help] 'help-for-help) |
51 | (define-key help-map [f1] 'help-for-help) | |
433ae6f6 RS |
52 | (define-key help-map "?" 'help-for-help) |
53 | ||
54 | (define-key help-map "\C-c" 'describe-copying) | |
55 | (define-key help-map "\C-d" 'describe-distribution) | |
56 | (define-key help-map "\C-w" 'describe-no-warranty) | |
76766f2d | 57 | (define-key help-map "\C-p" 'describe-project) |
122955bf | 58 | (define-key help-map "a" 'apropos-command) |
433ae6f6 RS |
59 | |
60 | (define-key help-map "b" 'describe-bindings) | |
61 | ||
62 | (define-key help-map "c" 'describe-key-briefly) | |
63 | (define-key help-map "k" 'describe-key) | |
64 | ||
65 | (define-key help-map "d" 'describe-function) | |
66 | (define-key help-map "f" 'describe-function) | |
67 | ||
7ee71cf1 RS |
68 | (define-key help-map "F" 'view-emacs-FAQ) |
69 | ||
433ae6f6 | 70 | (define-key help-map "i" 'info) |
4b08b7ed | 71 | (define-key help-map "4i" 'info-other-window) |
e5d77022 JB |
72 | (define-key help-map "\C-f" 'Info-goto-emacs-command-node) |
73 | (define-key help-map "\C-k" 'Info-goto-emacs-key-command-node) | |
32884eab | 74 | (define-key help-map "\C-i" 'info-lookup-symbol) |
433ae6f6 RS |
75 | |
76 | (define-key help-map "l" 'view-lossage) | |
77 | ||
78 | (define-key help-map "m" 'describe-mode) | |
754084bb | 79 | (define-key help-map "\C-m" 'view-order-manuals) |
433ae6f6 RS |
80 | |
81 | (define-key help-map "\C-n" 'view-emacs-news) | |
82 | (define-key help-map "n" 'view-emacs-news) | |
83 | ||
06b98c51 | 84 | (define-key help-map "p" 'finder-by-keyword) |
3e9c095d RS |
85 | (autoload 'finder-by-keyword "finder" |
86 | "Find packages matching a given keyword." t) | |
06b98c51 | 87 | |
4cbff657 DL |
88 | (define-key help-map "P" 'view-emacs-problems) |
89 | ||
433ae6f6 RS |
90 | (define-key help-map "s" 'describe-syntax) |
91 | ||
92 | (define-key help-map "t" 'help-with-tutorial) | |
93 | ||
94 | (define-key help-map "w" 'where-is) | |
95 | ||
96 | (define-key help-map "v" 'describe-variable) | |
97 | ||
2fc9d9f4 RS |
98 | (define-key help-map "q" 'help-quit) |
99 | ||
400a1b1f RS |
100 | (define-key help-mode-map [mouse-2] 'help-follow-mouse) |
101 | (define-key help-mode-map "\C-c\C-b" 'help-go-back) | |
102 | (define-key help-mode-map "\C-c\C-c" 'help-follow) | |
103 | (define-key help-mode-map "\t" 'help-next-ref) | |
104 | (define-key help-mode-map [backtab] 'help-previous-ref) | |
306a5e68 | 105 | (define-key help-mode-map [(shift tab)] 'help-previous-ref) |
400a1b1f RS |
106 | ;; Documentation only, since we use minor-mode-overriding-map-alist. |
107 | (define-key help-mode-map "\r" 'help-follow) | |
108 | ||
400a1b1f RS |
109 | (defvar help-xref-stack nil |
110 | "A stack of ways by which to return to help buffers after following xrefs. | |
4c45295b | 111 | Used by `help-follow' and `help-xref-go-back'. |
376b2a24 DL |
112 | An element looks like (POSITION FUNCTION ARGS...), where POSITION is |
113 | `(POINT . BUFFER-NAME)'. | |
114 | To use the element, do (apply FUNCTION ARGS) then goto the point in | |
115 | the named buffer.") | |
400a1b1f RS |
116 | (put 'help-xref-stack 'permanent-local t) |
117 | ||
118 | (defvar help-xref-stack-item nil | |
4c45295b KH |
119 | "An item for `help-follow' in this buffer to push onto `help-xref-stack'. |
120 | The format is (FUNCTION ARGS...).") | |
400a1b1f RS |
121 | (put 'help-xref-stack-item 'permanent-local t) |
122 | ||
123 | (setq-default help-xref-stack nil help-xref-stack-item nil) | |
507fb916 | 124 | |
73ea6d94 DL |
125 | (defcustom help-mode-hook nil |
126 | "Hook run by `help-mode'." | |
127 | :type 'hook | |
128 | :group 'help) | |
129 | ||
afaa65e4 | 130 | (defun help-mode () |
400a1b1f | 131 | "Major mode for viewing help text and navigating references in it. |
afaa65e4 KH |
132 | Entry to this mode runs the normal hook `help-mode-hook'. |
133 | Commands: | |
134 | \\{help-mode-map}" | |
135 | (interactive) | |
136 | (kill-all-local-variables) | |
137 | (use-local-map help-mode-map) | |
138 | (setq mode-name "Help") | |
139 | (setq major-mode 'help-mode) | |
507fb916 | 140 | (make-local-variable 'font-lock-defaults) |
400a1b1f | 141 | (setq font-lock-defaults nil) ; font-lock would defeat xref |
42499979 | 142 | (view-mode) |
f90b6922 RS |
143 | (make-local-variable 'view-no-disable-on-exit) |
144 | (setq view-no-disable-on-exit t) | |
afaa65e4 KH |
145 | (run-hooks 'help-mode-hook)) |
146 | ||
e48143f0 RS |
147 | (defun help-mode-setup () |
148 | (help-mode) | |
149 | (setq buffer-read-only nil)) | |
150 | ||
3d02beed | 151 | (add-hook 'temp-buffer-setup-hook 'help-mode-setup) |
e48143f0 RS |
152 | |
153 | (defun help-mode-finish () | |
4e1ede6c | 154 | (when (eq major-mode 'help-mode) |
400a1b1f RS |
155 | ;; View mode's read-only status of existing *Help* buffer is lost |
156 | ;; by with-output-to-temp-buffer. | |
157 | (toggle-read-only 1) | |
158 | (help-make-xrefs (current-buffer))) | |
01364a75 RS |
159 | (setq view-return-to-alist |
160 | (list (cons (selected-window) help-return-method)))) | |
21de5941 | 161 | |
3d02beed | 162 | (add-hook 'temp-buffer-show-hook 'help-mode-finish) |
21de5941 | 163 | |
2fc9d9f4 | 164 | (defun help-quit () |
3120a677 | 165 | "Just exit from the Help command's command loop." |
2fc9d9f4 RS |
166 | (interactive) |
167 | nil) | |
168 | ||
0634ea78 KH |
169 | (defun help-with-tutorial (&optional arg) |
170 | "Select the Emacs learn-by-doing tutorial. | |
da412772 | 171 | If there is a tutorial version written in the language |
71e9bd71 | 172 | of the selected language environment, that version is used. |
da412772 | 173 | If there's no tutorial in that language, `TUTORIAL' is selected. |
c822b44b | 174 | With arg, you are asked to choose which language." |
0634ea78 | 175 | (interactive "P") |
3060bf83 KH |
176 | (let ((lang (if arg |
177 | (read-language-name 'tutorial "Language: " "English") | |
178 | (if (get-language-info current-language-environment 'tutorial) | |
179 | current-language-environment | |
ad21fa07 RS |
180 | "English"))) |
181 | file filename) | |
3060bf83 | 182 | (setq filename (get-language-info lang 'tutorial)) |
7c9b148e | 183 | (setq file (expand-file-name (concat "~/" filename))) |
433ae6f6 RS |
184 | (delete-other-windows) |
185 | (if (get-file-buffer file) | |
186 | (switch-to-buffer (get-file-buffer file)) | |
187 | (switch-to-buffer (create-file-buffer file)) | |
188 | (setq buffer-file-name file) | |
189 | (setq default-directory (expand-file-name "~/")) | |
79058860 | 190 | (setq buffer-auto-save-file-name nil) |
0634ea78 | 191 | (insert-file-contents (expand-file-name filename data-directory)) |
433ae6f6 RS |
192 | (goto-char (point-min)) |
193 | (search-forward "\n<<") | |
194 | (beginning-of-line) | |
195 | (delete-region (point) (progn (end-of-line) (point))) | |
857a1de6 | 196 | (let ((n (- (window-height (selected-window)) |
433ae6f6 | 197 | (count-lines (point-min) (point)) |
857a1de6 | 198 | 6))) |
d0da2301 | 199 | (if (< n 12) |
857a1de6 KH |
200 | (newline n) |
201 | ;; Some people get confused by the large gap. | |
202 | (newline (/ n 2)) | |
203 | (insert "[Middle of page left blank for didactic purposes. " | |
204 | "Text continues below]") | |
205 | (newline (- n (/ n 2))))) | |
433ae6f6 RS |
206 | (goto-char (point-min)) |
207 | (set-buffer-modified-p nil)))) | |
208 | ||
c085b77c GM |
209 | (defun string-key-binding (key) |
210 | "Value is the binding of KEY in a string. | |
211 | If KEY is an event on a string, and that string has a `local-map' | |
212 | or `keymap' property, return the binding of KEY in the string's keymap." | |
213 | (let* ((defn nil) | |
214 | (start (when (vectorp key) | |
215 | (if (memq (aref key 0) '(mode-line header-line)) | |
216 | (event-start (aref key 1)) | |
4b29c200 GM |
217 | (and (consp (aref key 0)) |
218 | (event-start (aref key 0)))))) | |
c085b77c GM |
219 | (string-info (and (consp start) (nth 4 start)))) |
220 | (when string-info | |
221 | (let* ((string (car string-info)) | |
222 | (pos (cdr string-info)) | |
223 | (local-map (and (> pos 0) | |
224 | (< pos (length string)) | |
225 | (or (get-text-property pos 'local-map string) | |
226 | (get-text-property pos 'keymap string))))) | |
227 | (setq defn (and local-map (lookup-key local-map key))))) | |
35831732 GM |
228 | defn)) |
229 | ||
e88a2c59 RS |
230 | (defun describe-key-briefly (key &optional insert) |
231 | "Print the name of the function KEY invokes. KEY is a string. | |
232 | If INSERT (the prefix arg) is non-nil, insert the message in the buffer." | |
233 | (interactive "kDescribe key briefly: \nP") | |
fc558e4d RS |
234 | (save-excursion |
235 | (let ((modifiers (event-modifiers (aref key 0))) | |
e88a2c59 | 236 | (standard-output (if insert (current-buffer) t)) |
fc558e4d RS |
237 | window position) |
238 | ;; For a mouse button event, go to the button it applies to | |
239 | ;; to get the right key bindings. And go to the right place | |
240 | ;; in case the keymap depends on where you clicked. | |
241 | (if (or (memq 'click modifiers) (memq 'down modifiers) | |
242 | (memq 'drag modifiers)) | |
243 | (setq window (posn-window (event-start (aref key 0))) | |
244 | position (posn-point (event-start (aref key 0))))) | |
245 | (if (windowp window) | |
246 | (progn | |
247 | (set-buffer (window-buffer window)) | |
248 | (goto-char position))) | |
249 | ;; Ok, now look up the key and name the command. | |
c085b77c | 250 | (let ((defn (or (string-key-binding key) |
35831732 | 251 | (key-binding key))) |
e88a2c59 | 252 | (key-desc (key-description key))) |
fc558e4d | 253 | (if (or (null defn) (integerp defn)) |
e88a2c59 RS |
254 | (princ (format "%s is undefined" key-desc)) |
255 | (princ (format (if insert | |
0f2aa0e1 | 256 | "`%s' (`%s')" |
e88a2c59 RS |
257 | (if (windowp window) |
258 | "%s at that spot runs the command %s" | |
259 | "%s runs the command %s")) | |
260 | key-desc | |
261 | (if (symbolp defn) defn (prin1-to-string defn))))))))) | |
433ae6f6 | 262 | |
01364a75 RS |
263 | (defvar help-return-method nil |
264 | "What to do to \"exit\" the help buffer. | |
265 | This is a list | |
266 | (WINDOW . t) delete the selected window, go to WINDOW. | |
267 | (WINDOW . quit-window) do quit-window, then select WINDOW. | |
268 | (WINDOW BUF START POINT) display BUF at START, POINT, then select WINDOW.") | |
269 | ||
433ae6f6 RS |
270 | (defun print-help-return-message (&optional function) |
271 | "Display or return message saying how to restore windows after help command. | |
272 | Computes a message and applies the optional argument FUNCTION to it. | |
273 | If FUNCTION is nil, applies `message' to it, thus printing it." | |
274 | (and (not (get-buffer-window standard-output)) | |
d536293f | 275 | (let ((first-message |
a1c9f209 | 276 | (cond ((special-display-p (buffer-name standard-output)) |
01364a75 | 277 | (setq help-return-method (cons (selected-window) t)) |
d536293f RS |
278 | ;; If the help output buffer is a special display buffer, |
279 | ;; don't say anything about how to get rid of it. | |
280 | ;; First of all, the user will do that with the window | |
281 | ;; manager, not with Emacs. | |
282 | ;; Secondly, the buffer has not been displayed yet, | |
283 | ;; so we don't know whether its frame will be selected. | |
d536293f | 284 | nil) |
f3ad2fc8 GM |
285 | (display-buffer-reuse-frames |
286 | (setq help-return-method (cons (selected-window) | |
287 | 'quit-window)) | |
288 | nil) | |
d536293f | 289 | ((not (one-window-p t)) |
01364a75 RS |
290 | (setq help-return-method |
291 | (cons (selected-window) 'quit-window)) | |
d536293f RS |
292 | "Type \\[switch-to-buffer-other-window] RET to restore the other window.") |
293 | (pop-up-windows | |
01364a75 | 294 | (setq help-return-method (cons (selected-window) t)) |
d536293f RS |
295 | "Type \\[delete-other-windows] to remove help window.") |
296 | (t | |
01364a75 RS |
297 | (setq help-return-method |
298 | (list (selected-window) (window-buffer) | |
299 | (window-start) (window-point))) | |
d536293f RS |
300 | "Type \\[switch-to-buffer] RET to remove help window.")))) |
301 | (funcall (or function 'message) | |
302 | (concat | |
303 | (if first-message | |
376b2a24 DL |
304 | (substitute-command-keys first-message)) |
305 | (if first-message " ") | |
125a8d70 RS |
306 | ;; If the help buffer will go in a separate frame, |
307 | ;; it's no use mentioning a command to scroll, so don't. | |
a1c9f209 | 308 | (if (special-display-p (buffer-name standard-output)) |
125a8d70 | 309 | nil |
a1c9f209 | 310 | (if (same-window-p (buffer-name standard-output)) |
125a8d70 RS |
311 | ;; Say how to scroll this window. |
312 | (substitute-command-keys | |
313 | "\\[scroll-up] to scroll the help.") | |
314 | ;; Say how to scroll some other window. | |
6e7f5182 | 315 | (substitute-command-keys |
125a8d70 | 316 | "\\[scroll-other-window] to scroll the help.")))))))) |
433ae6f6 RS |
317 | |
318 | (defun describe-key (key) | |
319 | "Display documentation of the function invoked by KEY. KEY is a string." | |
320 | (interactive "kDescribe key: ") | |
fc558e4d RS |
321 | (save-excursion |
322 | (let ((modifiers (event-modifiers (aref key 0))) | |
323 | window position) | |
324 | ;; For a mouse button event, go to the button it applies to | |
325 | ;; to get the right key bindings. And go to the right place | |
326 | ;; in case the keymap depends on where you clicked. | |
327 | (if (or (memq 'click modifiers) (memq 'down modifiers) | |
328 | (memq 'drag modifiers)) | |
329 | (setq window (posn-window (event-start (aref key 0))) | |
330 | position (posn-point (event-start (aref key 0))))) | |
331 | (if (windowp window) | |
332 | (progn | |
333 | (set-buffer (window-buffer window)) | |
334 | (goto-char position))) | |
c085b77c | 335 | (let ((defn (or (string-key-binding key) (key-binding key)))) |
fc558e4d RS |
336 | (if (or (null defn) (integerp defn)) |
337 | (message "%s is undefined" (key-description key)) | |
338 | (with-output-to-temp-buffer "*Help*" | |
339 | (princ (key-description key)) | |
340 | (if (windowp window) | |
341 | (princ " at that spot")) | |
342 | (princ " runs the command ") | |
343 | (prin1 defn) | |
05f6170c | 344 | (princ "\n which is ") |
0ab0e672 | 345 | (describe-function-1 defn nil (interactive-p)) |
fc558e4d | 346 | (print-help-return-message))))))) |
433ae6f6 | 347 | |
ad023904 RS |
348 | (defun describe-mode () |
349 | "Display documentation of current major mode and minor modes. | |
40b3bdc1 RS |
350 | The major mode description comes first, followed by the minor modes, |
351 | each on a separate page. | |
352 | ||
433ae6f6 | 353 | For this to work correctly for a minor mode, the mode's indicator variable |
61c6b658 | 354 | \(listed in `minor-mode-alist') must also be a function whose documentation |
433ae6f6 | 355 | describes the minor mode." |
7192540b | 356 | (interactive) |
433ae6f6 | 357 | (with-output-to-temp-buffer "*Help*" |
40b3bdc1 RS |
358 | (when minor-mode-alist |
359 | (princ "The major mode is described first. | |
360 | For minor modes, see following pages.\n\n")) | |
361 | (princ mode-name) | |
362 | (princ " mode:\n") | |
363 | (princ (documentation major-mode)) | |
364 | (help-setup-xref (list #'help-xref-mode (current-buffer)) (interactive-p)) | |
365 | (let ((minor-modes minor-mode-alist)) | |
7192540b RS |
366 | (while minor-modes |
367 | (let* ((minor-mode (car (car minor-modes))) | |
ddbe99e0 | 368 | (indicator (car (cdr (car minor-modes))))) |
7192540b RS |
369 | ;; Document a minor mode if it is listed in minor-mode-alist, |
370 | ;; bound locally in this buffer, non-nil, and has a function | |
371 | ;; definition. | |
4675e266 GM |
372 | (if (and (boundp minor-mode) |
373 | (symbol-value minor-mode) | |
7192540b RS |
374 | (fboundp minor-mode)) |
375 | (let ((pretty-minor-mode minor-mode)) | |
376 | (if (string-match "-mode$" (symbol-name minor-mode)) | |
377 | (setq pretty-minor-mode | |
378 | (capitalize | |
379 | (substring (symbol-name minor-mode) | |
380 | 0 (match-beginning 0))))) | |
e95419a6 RS |
381 | (while (and indicator (symbolp indicator) |
382 | (boundp indicator) | |
383 | (not (eq indicator (symbol-value indicator)))) | |
7192540b | 384 | (setq indicator (symbol-value indicator))) |
40b3bdc1 | 385 | (princ "\n\f\n") |
2ef581f3 RS |
386 | (princ (format "%s minor mode (%s):\n" |
387 | pretty-minor-mode | |
388 | (if indicator | |
389 | (format "indicator%s" indicator) | |
390 | "no indicator"))) | |
40b3bdc1 | 391 | (princ (documentation minor-mode))))) |
7192540b | 392 | (setq minor-modes (cdr minor-modes)))) |
433ae6f6 RS |
393 | (print-help-return-message))) |
394 | ||
395 | ;; So keyboard macro definitions are documented correctly | |
396 | (fset 'defining-kbd-macro (symbol-function 'start-kbd-macro)) | |
397 | ||
398 | (defun describe-distribution () | |
399 | "Display info on how to obtain the latest version of GNU Emacs." | |
400 | (interactive) | |
401 | (find-file-read-only | |
1e6dacf6 | 402 | (expand-file-name "DISTRIB" data-directory))) |
433ae6f6 RS |
403 | |
404 | (defun describe-copying () | |
405 | "Display info on how you may redistribute copies of GNU Emacs." | |
406 | (interactive) | |
407 | (find-file-read-only | |
1e6dacf6 | 408 | (expand-file-name "COPYING" data-directory)) |
433ae6f6 RS |
409 | (goto-char (point-min))) |
410 | ||
76766f2d RS |
411 | (defun describe-project () |
412 | "Display info on the GNU project." | |
413 | (interactive) | |
414 | (find-file-read-only | |
869e248d | 415 | (expand-file-name "THE-GNU-PROJECT" data-directory)) |
76766f2d RS |
416 | (goto-char (point-min))) |
417 | ||
433ae6f6 RS |
418 | (defun describe-no-warranty () |
419 | "Display info on all the kinds of warranty Emacs does NOT have." | |
420 | (interactive) | |
421 | (describe-copying) | |
422 | (let (case-fold-search) | |
423 | (search-forward "NO WARRANTY") | |
424 | (recenter 0))) | |
425 | ||
61c6b658 | 426 | (defun describe-prefix-bindings () |
c7cba9cb RS |
427 | "Describe the bindings of the prefix used to reach this command. |
428 | The prefix described consists of all but the last event | |
429 | of the key sequence that ran this command." | |
61c6b658 | 430 | (interactive) |
ccc06dcc KH |
431 | (let* ((key (this-command-keys))) |
432 | (describe-bindings | |
433 | (if (stringp key) | |
434 | (substring key 0 (1- (length key))) | |
435 | (let ((prefix (make-vector (1- (length key)) nil)) | |
436 | (i 0)) | |
437 | (while (< i (length prefix)) | |
438 | (aset prefix i (aref key i)) | |
439 | (setq i (1+ i))) | |
440 | prefix))))) | |
c7cba9cb RS |
441 | ;; Make C-h after a prefix, when not specifically bound, |
442 | ;; run describe-prefix-bindings. | |
61c6b658 RS |
443 | (setq prefix-help-command 'describe-prefix-bindings) |
444 | ||
382d018a RS |
445 | (defun view-emacs-news (&optional arg) |
446 | "Display info on recent changes to Emacs. | |
f0753a5f | 447 | With numeric argument, display information on correspondingly older changes." |
382d018a | 448 | (interactive "P") |
f0753a5f GM |
449 | (let* ((arg (if arg (prefix-numeric-value arg) 0)) |
450 | (file (cond ((eq arg 0) "NEWS") | |
451 | ((eq arg 1) "ONEWS") | |
452 | (t | |
453 | (nth (- arg 2) | |
454 | (nreverse (directory-files data-directory | |
975f82c9 | 455 | nil "^ONEWS\\.[0-9]+$" |
f0753a5f GM |
456 | nil))))))) |
457 | (if file | |
458 | (find-file-read-only (expand-file-name file data-directory)) | |
459 | (error "No such old news")))) | |
433ae6f6 | 460 | |
754084bb GM |
461 | (defun view-order-manuals () |
462 | "Display the Emacs ORDERS file." | |
463 | (interactive) | |
38790755 GM |
464 | (find-file-read-only (expand-file-name "ORDERS" data-directory)) |
465 | (goto-address)) | |
754084bb | 466 | |
7ee71cf1 RS |
467 | (defun view-emacs-FAQ () |
468 | "Display the Emacs Frequently Asked Questions (FAQ) file." | |
469 | (interactive) | |
4a8adb0b | 470 | ;;; (find-file-read-only (expand-file-name "FAQ" data-directory)) |
279b772d | 471 | (info "(efaq)")) |
7ee71cf1 | 472 | |
4cbff657 DL |
473 | (defun view-emacs-problems () |
474 | "Display info on known problems with Emacs and possible workarounds." | |
475 | (interactive) | |
476 | (view-file (expand-file-name "PROBLEMS" data-directory))) | |
477 | ||
433ae6f6 RS |
478 | (defun view-lossage () |
479 | "Display last 100 input keystrokes." | |
480 | (interactive) | |
481 | (with-output-to-temp-buffer "*Help*" | |
298a7c8c RS |
482 | (princ (mapconcat (function (lambda (key) |
483 | (if (or (integerp key) | |
484 | (symbolp key) | |
485 | (listp key)) | |
486 | (single-key-description key) | |
487 | (prin1-to-string key nil)))) | |
488 | (recent-keys) | |
489 | " ")) | |
433ae6f6 RS |
490 | (save-excursion |
491 | (set-buffer standard-output) | |
492 | (goto-char (point-min)) | |
493 | (while (progn (move-to-column 50) (not (eobp))) | |
494 | (search-forward " " nil t) | |
613a39b9 RS |
495 | (insert "\n")) |
496 | (setq help-xref-stack nil | |
497 | help-xref-stack-item nil)) | |
433ae6f6 RS |
498 | (print-help-return-message))) |
499 | ||
2fc9d9f4 | 500 | (defalias 'help 'help-for-help) |
41b8542b | 501 | (make-help-screen help-for-help |
a30a106b | 502 | "a b c C f F C-f i I k C-k l L m n p s t v w C-c C-d C-n C-p C-w; ? for help:" |
a82e9c01 | 503 | "You have typed %THIS-KEY%, the help character. Type a Help option: |
efcce2d2 | 504 | \(Use SPC or DEL to scroll through this text. Type \\<help-map>\\[help-quit] to exit the Help command.) |
433ae6f6 | 505 | |
21ee8c42 RM |
506 | a command-apropos. Give a substring, and see a list of commands |
507 | (functions interactively callable) that contain | |
508 | that substring. See also the apropos command. | |
af6a9de9 RS |
509 | b describe-bindings. Display table of all key bindings. |
510 | c describe-key-briefly. Type a command key sequence; | |
21ee8c42 | 511 | it prints the function name that sequence runs. |
a30a106b RS |
512 | C describe-coding-system. This describes either a specific coding system |
513 | (if you type its name) or the coding systems currently in use | |
514 | (if you type just RET). | |
af6a9de9 | 515 | f describe-function. Type a function name and get documentation of it. |
21ee8c42 RM |
516 | C-f Info-goto-emacs-command-node. Type a function name; |
517 | it takes you to the Info node for that command. | |
af6a9de9 | 518 | i info. The info documentation reader. |
a30a106b RS |
519 | I describe-input-method. Describe a specific input method (if you type |
520 | its name) or the current input method (if you type just RET). | |
90a56040 KH |
521 | C-i info-lookup-symbol. Display the definition of a specific symbol |
522 | as found in the manual for the language this buffer is written in. | |
af6a9de9 | 523 | k describe-key. Type a command key sequence; |
21ee8c42 RM |
524 | it displays the full documentation. |
525 | C-k Info-goto-emacs-key-command-node. Type a command key sequence; | |
526 | it takes you to the Info node for the command bound to that key. | |
90a56040 | 527 | l view-lossage. Show last 100 characters you typed. |
5760471f | 528 | L describe-language-environment. This describes either a |
a30a106b RS |
529 | specific language environment (if you type its name) |
530 | or the current language environment (if you type just RET). | |
ed13681f KH |
531 | m describe-mode. Print documentation of current minor modes, |
532 | and the current major mode, including their special commands. | |
90a56040 | 533 | n view-emacs-news. Display news of recent Emacs changes. |
af6a9de9 RS |
534 | p finder-by-keyword. Find packages matching a given topic keyword. |
535 | s describe-syntax. Display contents of syntax table, plus explanations | |
536 | t help-with-tutorial. Select the Emacs learn-by-doing tutorial. | |
537 | v describe-variable. Type name of a variable; | |
21ee8c42 | 538 | it displays the variable's documentation and value. |
af6a9de9 | 539 | w where-is. Type command name; it prints which keystrokes |
21ee8c42 | 540 | invoke that command. |
a30a106b RS |
541 | |
542 | F Display the frequently asked questions file. | |
543 | h Display the HELLO file which illustrates various scripts. | |
544 | C-c Display Emacs copying permission (General Public License). | |
545 | C-d Display Emacs ordering information. | |
546 | C-n Display news of recent Emacs changes. | |
547 | C-p Display information about the GNU project. | |
548 | C-w Display information on absence of warranty for GNU Emacs." | |
41b8542b | 549 | help-map) |
433ae6f6 | 550 | |
433ae6f6 | 551 | (defun function-called-at-point () |
b2c85790 DL |
552 | "Return a function around point or else called by the list containing point. |
553 | If that doesn't give a function, return nil." | |
9a200f68 DL |
554 | (with-syntax-table emacs-lisp-mode-syntax-table |
555 | (or (condition-case () | |
556 | (save-excursion | |
557 | (or (not (zerop (skip-syntax-backward "_w"))) | |
558 | (eq (char-syntax (following-char)) ?w) | |
559 | (eq (char-syntax (following-char)) ?_) | |
560 | (forward-sexp -1)) | |
561 | (skip-chars-forward "'") | |
562 | (let ((obj (read (current-buffer)))) | |
563 | (and (symbolp obj) (fboundp obj) obj))) | |
564 | (error nil)) | |
565 | (condition-case () | |
566 | (save-excursion | |
567 | (save-restriction | |
568 | (narrow-to-region (max (point-min) | |
569 | (- (point) 1000)) (point-max)) | |
570 | ;; Move up to surrounding paren, then after the open. | |
571 | (backward-up-list 1) | |
572 | (forward-char 1) | |
573 | ;; If there is space here, this is probably something | |
574 | ;; other than a real Lisp function call, so ignore it. | |
575 | (if (looking-at "[ \t]") | |
576 | (error "Probably not a Lisp function call")) | |
577 | (let ((obj (read (current-buffer)))) | |
578 | (and (symbolp obj) (fboundp obj) obj)))) | |
579 | (error nil))))) | |
433ae6f6 | 580 | |
0f619a41 KH |
581 | (defvar symbol-file-load-history-loaded nil |
582 | "Non-nil means we have loaded the file `fns-VERSION.el' in `exec-directory'. | |
583 | That file records the part of `load-history' for preloaded files, | |
584 | which is cleared out before dumping to make Emacs smaller.") | |
585 | ||
50ca4be0 GM |
586 | (defun load-symbol-file-load-history () |
587 | "Load the file `fns-VERSION.el' in `exec-directory' if not already done. | |
588 | That file records the part of `load-history' for preloaded files, | |
589 | which is cleared out before dumping to make Emacs smaller." | |
0f619a41 KH |
590 | (unless symbol-file-load-history-loaded |
591 | (load (expand-file-name | |
592 | ;; fns-XX.YY.ZZ.el does not work on DOS filesystem. | |
593 | (if (eq system-type 'ms-dos) | |
594 | "fns.el" | |
595 | (format "fns-%s.el" emacs-version)) | |
596 | exec-directory) | |
597 | ;; The file name fns-%s.el already has a .el extension. | |
598 | nil nil t) | |
50ca4be0 GM |
599 | (setq symbol-file-load-history-loaded t))) |
600 | ||
601 | (defun symbol-file (function) | |
602 | "Return the input source from which FUNCTION was loaded. | |
603 | The value is normally a string that was passed to `load': | |
604 | either an absolute file name, or a library name | |
605 | \(with no directory name and no `.el' or `.elc' at the end). | |
606 | It can also be nil, if the definition is not associated with any file." | |
607 | (load-symbol-file-load-history) | |
ca5ed196 RS |
608 | (let ((files load-history) |
609 | file functions) | |
610 | (while files | |
611 | (if (memq function (cdr (car files))) | |
612 | (setq file (car (car files)) files nil)) | |
613 | (setq files (cdr files))) | |
614 | file)) | |
615 | ||
433ae6f6 RS |
616 | (defun describe-function (function) |
617 | "Display the full documentation of FUNCTION (a symbol)." | |
618 | (interactive | |
619 | (let ((fn (function-called-at-point)) | |
620 | (enable-recursive-minibuffers t) | |
621 | val) | |
622 | (setq val (completing-read (if fn | |
623 | (format "Describe function (default %s): " fn) | |
624 | "Describe function: ") | |
1bacc93e | 625 | obarray 'fboundp t nil nil (symbol-name fn))) |
433ae6f6 RS |
626 | (list (if (equal val "") |
627 | fn (intern val))))) | |
00d3de8e RS |
628 | (if function |
629 | (with-output-to-temp-buffer "*Help*" | |
630 | (prin1 function) | |
eea844b2 RS |
631 | ;; Use " is " instead of a colon so that |
632 | ;; it is easier to get out the function name using forward-sexp. | |
633 | (princ " is ") | |
0ab0e672 | 634 | (describe-function-1 function nil (interactive-p)) |
00d3de8e RS |
635 | (print-help-return-message) |
636 | (save-excursion | |
637 | (set-buffer standard-output) | |
00d3de8e RS |
638 | ;; Return the text we displayed. |
639 | (buffer-string))) | |
640 | (message "You didn't specify a function"))) | |
641 | ||
0ab0e672 | 642 | (defun describe-function-1 (function parens interactive-p) |
6016b6e4 RS |
643 | (let* ((def (if (symbolp function) |
644 | (symbol-function function) | |
645 | function)) | |
05f6170c KH |
646 | file-name string need-close |
647 | (beg (if (commandp def) "an interactive " "a "))) | |
648 | (setq string | |
649 | (cond ((or (stringp def) | |
650 | (vectorp def)) | |
651 | "a keyboard macro") | |
652 | ((subrp def) | |
d16296bb DL |
653 | (if (eq 'unevalled (cdr (subr-arity def))) |
654 | (concat beg "special form") | |
655 | (concat beg "built-in function"))) | |
05f6170c KH |
656 | ((byte-code-function-p def) |
657 | (concat beg "compiled Lisp function")) | |
658 | ((symbolp def) | |
3f8309db RS |
659 | (while (symbolp (symbol-function def)) |
660 | (setq def (symbol-function def))) | |
213f4eae | 661 | (format "an alias for `%s'" def)) |
05f6170c KH |
662 | ((eq (car-safe def) 'lambda) |
663 | (concat beg "Lisp function")) | |
664 | ((eq (car-safe def) 'macro) | |
665 | "a Lisp macro") | |
666 | ((eq (car-safe def) 'mocklisp) | |
667 | "a mocklisp function") | |
668 | ((eq (car-safe def) 'autoload) | |
669 | (setq file-name (nth 1 def)) | |
aed2b2cd | 670 | (format "%s autoloaded %s" |
05f6170c | 671 | (if (commandp def) "an interactive" "an") |
aed2b2cd AS |
672 | (if (eq (nth 4 def) 'keymap) "keymap" |
673 | (if (nth 4 def) "Lisp macro" "Lisp function")) | |
05f6170c | 674 | )) |
b89d72a1 RS |
675 | ;; perhaps use keymapp here instead |
676 | ((eq (car-safe def) 'keymap) | |
677 | (let ((is-full nil) | |
678 | (elts (cdr-safe def))) | |
679 | (while elts | |
680 | (if (char-table-p (car-safe elts)) | |
681 | (setq is-full t | |
682 | elts nil)) | |
683 | (setq elts (cdr-safe elts))) | |
684 | (if is-full | |
685 | "a full keymap" | |
686 | "a sparse keymap"))) | |
05f6170c KH |
687 | (t ""))) |
688 | (when (and parens (not (equal string ""))) | |
689 | (setq need-close t) | |
690 | (princ "(")) | |
691 | (princ string) | |
b2c85790 DL |
692 | (with-current-buffer "*Help*" |
693 | (save-excursion | |
694 | (save-match-data | |
695 | (if (re-search-backward "alias for `\\([^`']+\\)'" nil t) | |
d77dae5c DL |
696 | (help-xref-button 1 #'describe-function def |
697 | "mouse-2, RET: describe this function"))))) | |
05f6170c | 698 | (or file-name |
0f619a41 | 699 | (setq file-name (symbol-file function))) |
05f6170c KH |
700 | (if file-name |
701 | (progn | |
702 | (princ " in `") | |
703 | ;; We used to add .el to the file name, | |
704 | ;; but that's completely wrong when the user used load-file. | |
705 | (princ file-name) | |
2676e099 DL |
706 | (princ "'") |
707 | ;; Make a hyperlink to the library. | |
708 | (with-current-buffer "*Help*" | |
709 | (save-excursion | |
710 | (re-search-backward "`\\([^`']+\\)'" nil t) | |
7dcf1127 RS |
711 | (help-xref-button 1 #'(lambda (arg) |
712 | (let ((location | |
713 | (find-function-noselect arg))) | |
582b6241 RS |
714 | (pop-to-buffer (car location)) |
715 | (goto-char (cdr location)))) | |
d77dae5c DL |
716 | function |
717 | "mouse-2, RET: find function's definition"))))) | |
05f6170c KH |
718 | (if need-close (princ ")")) |
719 | (princ ".") | |
720 | (terpri) | |
3f8309db RS |
721 | ;; Handle symbols aliased to other symbols. |
722 | (setq def (indirect-function def)) | |
723 | ;; If definition is a macro, find the function inside it. | |
724 | (if (eq (car-safe def) 'macro) | |
725 | (setq def (cdr def))) | |
726 | (let ((arglist (cond ((byte-code-function-p def) | |
727 | (car (append def nil))) | |
728 | ((eq (car-safe def) 'lambda) | |
729 | (nth 1 def)) | |
f0d0fb19 DL |
730 | ((and (eq (car-safe def) 'autoload) |
731 | (not (eq (nth 4 def) 'keymap))) | |
732 | (concat "[Arg list not available until " | |
733 | "function definition is loaded.]")) | |
3f8309db | 734 | (t t)))) |
f0d0fb19 DL |
735 | (cond ((listp arglist) |
736 | (princ (cons (if (symbolp function) function "anonymous") | |
737 | (mapcar (lambda (arg) | |
738 | (if (memq arg '(&optional &rest)) | |
739 | arg | |
740 | (intern (upcase (symbol-name arg))))) | |
741 | arglist))) | |
742 | (terpri)) | |
743 | ((stringp arglist) | |
744 | (princ arglist) | |
745 | (terpri)))) | |
05f6170c KH |
746 | (let ((doc (documentation function))) |
747 | (if doc | |
748 | (progn (terpri) | |
749 | (princ doc) | |
d16296bb DL |
750 | (if (subrp (symbol-function function)) |
751 | (with-current-buffer standard-output | |
752 | (beginning-of-line) | |
753 | ;; Builtins get the calling sequence at the end of | |
754 | ;; the doc string. Move it to the same place as | |
755 | ;; for other functions. | |
9c50afce DL |
756 | |
757 | ;; In cases where `function' has been fset to a | |
758 | ;; subr we can't search for function's name in | |
759 | ;; the doc string. Kluge round that using the | |
760 | ;; printed representation. The arg list then | |
761 | ;; shows the wrong function name, but that | |
762 | ;; might be a useful hint. | |
763 | (let* ((rep (prin1-to-string def)) | |
764 | (name (progn | |
765 | (string-match " \\([^ ]+\\)>$" rep) | |
766 | (match-string 1 rep)))) | |
0383ed60 | 767 | (if (looking-at (format "(%s[ )]" (regexp-quote name))) |
9c50afce DL |
768 | (let ((start (point-marker))) |
769 | (goto-char (point-min)) | |
770 | (forward-paragraph) | |
771 | (insert-buffer-substring (current-buffer) start) | |
772 | (insert ?\n) | |
773 | (delete-region (1- start) (point-max))) | |
774 | (goto-char (point-min)) | |
775 | (forward-paragraph) | |
776 | (insert | |
777 | "[Missing arglist. Please make a bug report.]\n"))) | |
d16296bb | 778 | (goto-char (point-max)))) |
f0d0fb19 DL |
779 | (help-setup-xref (list #'describe-function function) |
780 | interactive-p)) | |
05f6170c KH |
781 | (princ "not documented"))))) |
782 | ||
433ae6f6 | 783 | (defun variable-at-point () |
b2c85790 DL |
784 | "Return the bound variable symbol found around point. |
785 | Return 0 if there is no such symbol." | |
433ae6f6 | 786 | (condition-case () |
9a200f68 DL |
787 | (with-syntax-table emacs-lisp-mode-syntax-table |
788 | (save-excursion | |
789 | (or (not (zerop (skip-syntax-backward "_w"))) | |
790 | (eq (char-syntax (following-char)) ?w) | |
791 | (eq (char-syntax (following-char)) ?_) | |
792 | (forward-sexp -1)) | |
793 | (skip-chars-forward "'") | |
794 | (let ((obj (read (current-buffer)))) | |
795 | (or (and (symbolp obj) (boundp obj) obj) | |
796 | 0)))) | |
00d3de8e | 797 | (error 0))) |
433ae6f6 | 798 | |
2e48ba18 GM |
799 | (defun help-xref-on-pp (from to) |
800 | "Add xrefs for symbols in `pp's output between FROM and TO." | |
801 | (let ((ost (syntax-table))) | |
802 | (unwind-protect | |
803 | (save-excursion | |
804 | (save-restriction | |
805 | (set-syntax-table emacs-lisp-mode-syntax-table) | |
806 | (narrow-to-region from to) | |
807 | (goto-char (point-min)) | |
808 | (while (not (eobp)) | |
809 | (cond | |
810 | ((looking-at "\"") (forward-sexp 1)) | |
811 | ((looking-at "#<") (search-forward ">" nil 'move)) | |
812 | ((looking-at "\\(\\(\\sw\\|\\s_\\)+\\)") | |
4e1ede6c | 813 | (let* ((sym (intern-soft (match-string 1))) |
2e48ba18 | 814 | (fn (cond ((fboundp sym) #'describe-function) |
820ad5e7 DL |
815 | ((or (memq sym '(t nil)) |
816 | (keywordp sym)) | |
817 | nil) | |
818 | ((and sym (boundp sym)) | |
819 | #'describe-variable)))) | |
2e48ba18 GM |
820 | (when fn (help-xref-button 1 fn sym))) |
821 | (goto-char (match-end 1))) | |
822 | (t (forward-char 1)))))) | |
823 | (set-syntax-table ost)))) | |
824 | ||
4e1ede6c | 825 | (defun describe-variable (variable &optional buffer) |
433ae6f6 | 826 | "Display the full documentation of VARIABLE (a symbol). |
4e1ede6c SM |
827 | Returns the documentation as a string, also. |
828 | If VARIABLE has a buffer-local value in BUFFER (default to the current buffer), | |
829 | it is displayed along with the global value." | |
830 | (interactive | |
433ae6f6 RS |
831 | (let ((v (variable-at-point)) |
832 | (enable-recursive-minibuffers t) | |
833 | val) | |
00d3de8e | 834 | (setq val (completing-read (if (symbolp v) |
820ad5e7 DL |
835 | (format |
836 | "Describe variable (default %s): " v) | |
433ae6f6 | 837 | "Describe variable: ") |
d5645846 KH |
838 | obarray 'boundp t nil nil |
839 | (if (symbolp v) (symbol-name v)))) | |
433ae6f6 RS |
840 | (list (if (equal val "") |
841 | v (intern val))))) | |
4e1ede6c SM |
842 | (unless (bufferp buffer) (setq buffer (current-buffer))) |
843 | (if (not (symbolp variable)) | |
844 | (message "You did not specify a variable") | |
845 | (let (valvoid) | |
846 | (with-current-buffer buffer | |
9a656d19 RS |
847 | (with-output-to-temp-buffer "*Help*" |
848 | (prin1 variable) | |
849 | (if (not (boundp variable)) | |
850 | (progn | |
851 | (princ " is void") | |
9a656d19 | 852 | (setq valvoid t)) |
2e48ba18 GM |
853 | (let ((val (symbol-value variable))) |
854 | (with-current-buffer standard-output | |
855 | (princ "'s value is ") | |
856 | (terpri) | |
857 | (let ((from (point))) | |
858 | (pp val) | |
859 | (help-xref-on-pp from (point)))))) | |
860 | (terpri) | |
9a656d19 RS |
861 | (if (local-variable-p variable) |
862 | (progn | |
863 | (princ (format "Local in buffer %s; " (buffer-name))) | |
864 | (if (not (default-boundp variable)) | |
865 | (princ "globally void") | |
2e48ba18 GM |
866 | (let ((val (default-value variable))) |
867 | (with-current-buffer standard-output | |
868 | (princ "global value is ") | |
869 | (terpri) | |
9a200f68 DL |
870 | ;; Fixme: pp can take an age if you happen to |
871 | ;; ask for a very large expression. We should | |
872 | ;; probably print it raw once and check it's a | |
873 | ;; sensible size before prettyprinting. -- fx | |
2e48ba18 GM |
874 | (let ((from (point))) |
875 | (pp val) | |
876 | (help-xref-on-pp from (point)))))) | |
9a656d19 RS |
877 | (terpri))) |
878 | (terpri) | |
4e1ede6c | 879 | (with-current-buffer standard-output |
9a656d19 RS |
880 | (if (> (count-lines (point-min) (point-max)) 10) |
881 | (progn | |
d365421f GM |
882 | ;; Note that setting the syntax table like below |
883 | ;; makes forward-sexp move over a `'s' at the end | |
884 | ;; of a symbol. | |
14cc00ad | 885 | (set-syntax-table emacs-lisp-mode-syntax-table) |
9a656d19 RS |
886 | (goto-char (point-min)) |
887 | (if valvoid | |
888 | (forward-line 1) | |
889 | (forward-sexp 1) | |
890 | (delete-region (point) (progn (end-of-line) (point))) | |
d365421f | 891 | (insert " value is shown below.\n\n") |
9a656d19 RS |
892 | (save-excursion |
893 | (insert "\n\nValue:")))))) | |
894 | (princ "Documentation:") | |
895 | (terpri) | |
896 | (let ((doc (documentation-property variable 'variable-documentation))) | |
897 | (princ (or doc "not documented as a variable."))) | |
4e1ede6c SM |
898 | (help-setup-xref (list #'describe-variable variable (current-buffer)) |
899 | (interactive-p)) | |
900 | ||
7e824765 | 901 | ;; Make a link to customize if this variable can be customized. |
be66e132 | 902 | ;; Note, it is not reliable to test only for a custom-type property |
4f103eaa RS |
903 | ;; because those are only present after the var's definition |
904 | ;; has been loaded. | |
96757035 DL |
905 | (if (or (get variable 'custom-type) ; after defcustom |
906 | (get variable 'custom-loads) ; from loaddefs.el | |
907 | (get variable 'standard-value)) ; from cus-start.el | |
7e824765 RS |
908 | (let ((customize-label "customize")) |
909 | (terpri) | |
910 | (terpri) | |
911 | (princ (concat "You can " customize-label " this variable.")) | |
912 | (with-current-buffer "*Help*" | |
913 | (save-excursion | |
4e1ede6c | 914 | (re-search-backward |
7e824765 | 915 | (concat "\\(" customize-label "\\)") nil t) |
950cf06f DL |
916 | (help-xref-button 1 (lambda (v) |
917 | (if help-xref-stack | |
918 | (pop help-xref-stack)) | |
919 | (customize-variable v)) | |
d77dae5c | 920 | variable |
950cf06f | 921 | "mouse-2, RET: customize variable"))))) |
3476e159 DL |
922 | ;; Make a hyperlink to the library if appropriate. (Don't |
923 | ;; change the format of the buffer's initial line in case | |
924 | ;; anything expects the current format.) | |
0f619a41 | 925 | (let ((file-name (symbol-file variable))) |
3476e159 | 926 | (when file-name |
5f373960 | 927 | (princ "\n\nDefined in `") |
3476e159 | 928 | (princ file-name) |
5f373960 | 929 | (princ "'.") |
3476e159 DL |
930 | (with-current-buffer "*Help*" |
931 | (save-excursion | |
5f373960 | 932 | (re-search-backward "`\\([^`']+\\)'" nil t) |
d77dae5c DL |
933 | (help-xref-button |
934 | 1 (lambda (arg) | |
935 | (let ((location | |
936 | (find-variable-noselect arg))) | |
937 | (pop-to-buffer (car location)) | |
938 | (goto-char (cdr location)))) | |
939 | variable "mouse-2, RET: find variable's definition"))))) | |
7e824765 | 940 | |
9a656d19 RS |
941 | (print-help-return-message) |
942 | (save-excursion | |
943 | (set-buffer standard-output) | |
9a656d19 | 944 | ;; Return the text we displayed. |
4e1ede6c | 945 | (buffer-string))))))) |
433ae6f6 | 946 | |
4c45295b | 947 | (defun describe-bindings (&optional prefix buffer) |
a8ad43aa RS |
948 | "Show a list of all defined keys, and their definitions. |
949 | We put that list in a buffer, and display the buffer. | |
950 | ||
951 | The optional argument PREFIX, if non-nil, should be a key sequence; | |
4c45295b KH |
952 | then we display only bindings that start with that prefix. |
953 | The optional argument BUFFER specifies which buffer's bindings | |
954 | to display (default, the current buffer)." | |
a249d3a0 | 955 | (interactive "P") |
4c45295b KH |
956 | (or buffer (setq buffer (current-buffer))) |
957 | (with-current-buffer buffer | |
958 | (describe-bindings-internal nil prefix)) | |
613a39b9 | 959 | (with-current-buffer "*Help*" |
4c45295b KH |
960 | (help-setup-xref (list #'describe-bindings prefix buffer) |
961 | (interactive-p)))) | |
a8ad43aa | 962 | |
e88a2c59 | 963 | (defun where-is (definition &optional insert) |
b2c85790 | 964 | "Print message listing key sequences that invoke the command DEFINITION. |
e88a2c59 RS |
965 | Argument is a command definition, usually a symbol with a function definition. |
966 | If INSERT (the prefix arg) is non-nil, insert the message in the buffer." | |
54c0b967 RS |
967 | (interactive |
968 | (let ((fn (function-called-at-point)) | |
969 | (enable-recursive-minibuffers t) | |
970 | val) | |
971 | (setq val (completing-read (if fn | |
972 | (format "Where is command (default %s): " fn) | |
973 | "Where is command: ") | |
d5f65532 | 974 | obarray 'commandp t)) |
54c0b967 | 975 | (list (if (equal val "") |
e88a2c59 RS |
976 | fn (intern val)) |
977 | current-prefix-arg))) | |
54c0b967 | 978 | (let* ((keys (where-is-internal definition overriding-local-map nil nil)) |
e88a2c59 RS |
979 | (keys1 (mapconcat 'key-description keys ", ")) |
980 | (standard-output (if insert (current-buffer) t))) | |
981 | (if insert | |
982 | (if (> (length keys1) 0) | |
983 | (princ (format "%s (%s)" keys1 definition)) | |
984 | (princ (format "M-x %s RET" definition))) | |
985 | (if (> (length keys1) 0) | |
986 | (princ (format "%s is on %s" definition keys1)) | |
987 | (princ (format "%s is not on any key" definition))))) | |
54c0b967 RS |
988 | nil) |
989 | ||
a130d829 | 990 | (defun locate-library (library &optional nosuffix path interactive-call) |
2747503c | 991 | "Show the precise file name of Emacs library LIBRARY. |
433ae6f6 RS |
992 | This command searches the directories in `load-path' like `M-x load-library' |
993 | to find the file that `M-x load-library RET LIBRARY RET' would load. | |
994 | Optional second arg NOSUFFIX non-nil means don't add suffixes `.elc' or `.el' | |
9dc176a0 RS |
995 | to the specified name LIBRARY. |
996 | ||
997 | If the optional third arg PATH is specified, that list of directories | |
b2c85790 DL |
998 | is used instead of `load-path'. |
999 | ||
1000 | When called from a program, the file name is normaly returned as a | |
1001 | string. When run interactively, the argument INTERACTIVE-CALL is t, | |
1002 | and the file name is displayed in the echo area." | |
a130d829 RS |
1003 | (interactive (list (read-string "Locate library: ") |
1004 | nil nil | |
1005 | t)) | |
dd557bb8 | 1006 | (let (result) |
a130d829 | 1007 | (catch 'answer |
e3e36d74 | 1008 | (mapc |
a1c9f209 | 1009 | (lambda (dir) |
e3e36d74 | 1010 | (mapc |
a1c9f209 EN |
1011 | (lambda (suf) |
1012 | (let ((try (expand-file-name (concat library suf) dir))) | |
1013 | (and (file-readable-p try) | |
1014 | (null (file-directory-p try)) | |
1015 | (progn | |
1016 | (setq result try) | |
1017 | (throw 'answer try))))) | |
1018 | (if nosuffix | |
1019 | '("") | |
dd557bb8 KH |
1020 | '(".elc" ".el" "") |
1021 | ;;; load doesn't handle this yet. | |
1022 | ;;; (let ((basic '(".elc" ".el" "")) | |
1023 | ;;; (compressed '(".Z" ".gz" ""))) | |
1024 | ;;; ;; If autocompression mode is on, | |
1025 | ;;; ;; consider all combinations of library suffixes | |
1026 | ;;; ;; and compression suffixes. | |
1027 | ;;; (if (rassq 'jka-compr-handler file-name-handler-alist) | |
1028 | ;;; (apply 'nconc | |
1029 | ;;; (mapcar (lambda (compelt) | |
1030 | ;;; (mapcar (lambda (baselt) | |
1031 | ;;; (concat baselt compelt)) | |
1032 | ;;; basic)) | |
1033 | ;;; compressed)) | |
1034 | ;;; basic)) | |
1035 | ))) | |
a130d829 RS |
1036 | (or path load-path))) |
1037 | (and interactive-call | |
1038 | (if result | |
1039 | (message "Library is file %s" result) | |
1040 | (message "No library %s in search path" library))) | |
1041 | result)) | |
1a06eabd | 1042 | |
400a1b1f RS |
1043 | \f |
1044 | ;;; Grokking cross-reference information in doc strings and | |
1045 | ;;; hyperlinking it. | |
1046 | ||
1047 | ;; This may have some scope for extension and the same or something | |
1048 | ;; similar should be done for widget doc strings, which currently use | |
1049 | ;; another mechanism. | |
1050 | ||
1051 | (defcustom help-highlight-p t | |
1052 | "*If non-nil, `help-make-xrefs' highlight cross-references. | |
1053 | Under a window system it highlights them with face defined by | |
510df933 | 1054 | `help-highlight-face'." |
400a1b1f RS |
1055 | :group 'help |
1056 | :version "20.3" | |
1057 | :type 'boolean) | |
1058 | ||
1059 | (defcustom help-highlight-face 'underline | |
1060 | "Face used by `help-make-xrefs' to highlight cross-references. | |
1061 | Must be previously-defined." | |
1062 | :group 'help | |
1063 | :version "20.3" | |
7f082394 | 1064 | :type 'face) |
400a1b1f | 1065 | |
4607e12b | 1066 | (defvar help-back-label (purecopy "[back]") |
400a1b1f RS |
1067 | "Label to use by `help-make-xrefs' for the go-back reference.") |
1068 | ||
4607e12b DL |
1069 | (defconst help-xref-symbol-regexp |
1070 | (purecopy (concat "\\(\\<\\(\\(variable\\|option\\)\\|" | |
1071 | "\\(function\\|command\\)\\|" | |
950cf06f | 1072 | "\\(face\\)\\|" |
4607e12b DL |
1073 | "\\(symbol\\)\\)\\s-+\\)?" |
1074 | ;; Note starting with word-syntax character: | |
1075 | "`\\(\\sw\\(\\sw\\|\\s_\\)+\\)'")) | |
400a1b1f RS |
1076 | "Regexp matching doc string references to symbols. |
1077 | ||
1078 | The words preceding the quoted symbol can be used in doc strings to | |
1079 | distinguish references to variables, functions and symbols.") | |
1080 | ||
cba62b78 | 1081 | (defconst help-xref-mule-regexp nil |
5b7a8c46 | 1082 | "Regexp matching doc string references to MULE-related keywords. |
cba62b78 | 1083 | |
5b7a8c46 EZ |
1084 | It is usually nil, and is temporarily bound to an appropriate regexp |
1085 | when help commands related to multilingual environment (e.g., | |
1086 | `describe-coding-system') are invoked.") | |
cba62b78 KH |
1087 | |
1088 | ||
4607e12b DL |
1089 | (defconst help-xref-info-regexp |
1090 | (purecopy "\\<[Ii]nfo[ \t\n]+node[ \t\n]+`\\([^']+\\)'") | |
400a1b1f RS |
1091 | "Regexp matching doc string references to an Info node.") |
1092 | ||
1093 | (defun help-setup-xref (item interactive-p) | |
1094 | "Invoked from commands using the \"*Help*\" buffer to install some xref info. | |
1095 | ||
4c45295b | 1096 | ITEM is a (FUNCTION . ARGS) pair appropriate for recreating the help |
400a1b1f RS |
1097 | buffer after following a reference. INTERACTIVE-P is non-nil if the |
1098 | calling command was invoked interactively. In this case the stack of | |
1099 | items for help buffer \"back\" buttons is cleared." | |
1100 | (if interactive-p | |
1101 | (setq help-xref-stack nil)) | |
1102 | (setq help-xref-stack-item item)) | |
1103 | ||
376b2a24 DL |
1104 | (defvar help-xref-following nil |
1105 | "Non-nil when following a help cross-reference.") | |
1106 | ||
400a1b1f RS |
1107 | (defun help-make-xrefs (&optional buffer) |
1108 | "Parse and hyperlink documentation cross-references in the given BUFFER. | |
1109 | ||
1110 | Find cross-reference information in a buffer and, if | |
1111 | `help-highlight-p' is non-nil, highlight it with face defined by | |
1112 | `help-highlight-face'; activate such cross references for selection | |
1113 | with `help-follow'. Cross-references have the canonical form `...' | |
1114 | and the type of reference may be disambiguated by the preceding | |
1115 | word(s) used in `help-xref-symbol-regexp'. | |
1116 | ||
cba62b78 | 1117 | If the variable `help-xref-mule-regexp' is non-nil, find also |
5b7a8c46 EZ |
1118 | cross-reference information related to multilingual environment |
1119 | \(e.g., coding-systems). This variable is also used to disambiguate | |
1120 | the type of reference as the same way as `help-xref-symbol-regexp'. | |
cba62b78 | 1121 | |
400a1b1f RS |
1122 | A special reference `back' is made to return back through a stack of |
1123 | help buffers. Variable `help-back-label' specifies the text for | |
1124 | that." | |
1125 | (interactive "b") | |
1126 | (save-excursion | |
1127 | (set-buffer (or buffer (current-buffer))) | |
1128 | (goto-char (point-min)) | |
1129 | ;; Skip the header-type info, though it might be useful to parse | |
1130 | ;; it at some stage (e.g. "function in `library'"). | |
1131 | (forward-paragraph) | |
1132 | (let ((old-modified (buffer-modified-p))) | |
1133 | (let ((stab (syntax-table)) | |
1134 | (case-fold-search t) | |
1135 | (inhibit-read-only t)) | |
1136 | (set-syntax-table emacs-lisp-mode-syntax-table) | |
1137 | ;; The following should probably be abstracted out. | |
1138 | (unwind-protect | |
1139 | (progn | |
f58790da RS |
1140 | ;; Info references |
1141 | (save-excursion | |
1142 | (while (re-search-forward help-xref-info-regexp nil t) | |
1143 | (let ((data (match-string 1))) | |
1144 | (save-match-data | |
1145 | (unless (string-match "^([^)]+)" data) | |
1146 | (setq data (concat "(emacs)" data)))) | |
d77dae5c DL |
1147 | (help-xref-button 1 #'info data |
1148 | "mouse-2, RET: read this Info node")))) | |
cba62b78 KH |
1149 | ;; Mule related keywords. Do this before trying |
1150 | ;; `help-xref-symbol-regexp' because some of Mule | |
1151 | ;; keywords have variable or function definitions. | |
1152 | (if help-xref-mule-regexp | |
1153 | (save-excursion | |
1154 | (while (re-search-forward help-xref-mule-regexp nil t) | |
96ede6b2 | 1155 | (let* ((data (match-string 7)) |
cba62b78 KH |
1156 | (sym (intern-soft data))) |
1157 | (cond | |
1158 | ((match-string 3) ; coding system | |
880c345e | 1159 | (and sym (coding-system-p sym) |
cba62b78 | 1160 | (help-xref-button |
96ede6b2 | 1161 | 7 #'describe-coding-system sym |
cba62b78 KH |
1162 | "mouse-2, RET: describe this coding system"))) |
1163 | ((match-string 4) ; input method | |
1164 | (and (assoc data input-method-alist) | |
1165 | (help-xref-button | |
96ede6b2 | 1166 | 7 #'describe-input-method data |
cba62b78 | 1167 | "mouse-2, RET: describe this input method"))) |
96ede6b2 KH |
1168 | ((or (match-string 5) (match-string 6)) ; charset |
1169 | (and sym (charsetp sym) | |
1170 | (help-xref-button | |
1171 | 7 #'describe-character-set sym | |
1172 | "mouse-2, RET: describe this character set"))) | |
1173 | ((assoc data input-method-alist) | |
1174 | (help-xref-button | |
1175 | 7 #'describe-input-method data | |
1176 | "mouse-2, RET: describe this input method")) | |
880c345e | 1177 | ((and sym (coding-system-p sym)) |
cba62b78 | 1178 | (help-xref-button |
96ede6b2 | 1179 | 7 #'describe-coding-system sym |
cba62b78 | 1180 | "mouse-2, RET: describe this coding system")) |
96ede6b2 | 1181 | ((and sym (charsetp sym)) |
cba62b78 | 1182 | (help-xref-button |
96ede6b2 KH |
1183 | 7 #'describe-character-set sym |
1184 | "mouse-2, RET: describe this character set"))))))) | |
400a1b1f RS |
1185 | ;; Quoted symbols |
1186 | (save-excursion | |
1187 | (while (re-search-forward help-xref-symbol-regexp nil t) | |
950cf06f | 1188 | (let* ((data (match-string 7)) |
400a1b1f RS |
1189 | (sym (intern-soft data))) |
1190 | (if sym | |
1191 | (cond | |
1192 | ((match-string 3) ; `variable' &c | |
1193 | (and (boundp sym) ; `variable' doesn't ensure | |
1194 | ; it's actually bound | |
d77dae5c | 1195 | (help-xref-button |
950cf06f | 1196 | 7 #'describe-variable sym |
d77dae5c | 1197 | "mouse-2, RET: describe this variable"))) |
400a1b1f RS |
1198 | ((match-string 4) ; `function' &c |
1199 | (and (fboundp sym) ; similarly | |
d77dae5c | 1200 | (help-xref-button |
950cf06f | 1201 | 7 #'describe-function sym |
d77dae5c | 1202 | "mouse-2, RET: describe this function"))) |
950cf06f DL |
1203 | ((match-string 5) ; `face' |
1204 | (and (facep sym) | |
1205 | (help-xref-button 7 #'describe-face sym | |
1206 | "mouse-2, RET: describe this face"))) | |
1207 | ((match-string 6)) ; nothing for symbol | |
d77dae5c | 1208 | ((and (boundp sym) (fboundp sym)) |
400a1b1f RS |
1209 | ;; We can't intuit whether to use the |
1210 | ;; variable or function doc -- supply both. | |
d77dae5c | 1211 | (help-xref-button |
950cf06f | 1212 | 7 #'help-xref-interned sym |
d77dae5c DL |
1213 | "mouse-2, RET: describe this symbol")) |
1214 | ((boundp sym) | |
1215 | (help-xref-button | |
950cf06f | 1216 | 7 #'describe-variable sym |
d77dae5c DL |
1217 | "mouse-2, RET: describe this variable")) |
1218 | ((fboundp sym) | |
1219 | (help-xref-button | |
950cf06f DL |
1220 | 7 #'describe-function sym |
1221 | "mouse-2, RET: describe this function")) | |
1222 | ((facep sym) | |
1223 | (help-xref-button | |
1224 | 7 #'describe-face sym))))))) | |
400a1b1f | 1225 | ;; An obvious case of a key substitution: |
4e1ede6c | 1226 | (save-excursion |
b2c85790 DL |
1227 | (while (re-search-forward |
1228 | ;; Assume command name is only word characters | |
1229 | ;; and dashes to get things like `use M-x foo.'. | |
1230 | "\\<M-x\\s-+\\(\\sw\\(\\sw\\|-\\)+\\)" nil t) | |
400a1b1f RS |
1231 | (let ((sym (intern-soft (match-string 1)))) |
1232 | (if (fboundp sym) | |
d77dae5c DL |
1233 | (help-xref-button |
1234 | 1 #'describe-function sym | |
1235 | "mouse-2, RET: describe this command"))))) | |
ff3453e4 DL |
1236 | ;; Look for commands in whole keymap substitutions: |
1237 | (save-excursion | |
9b49f910 RS |
1238 | ;; Make sure to find the first keymap. |
1239 | (goto-char (point-min)) | |
ff3453e4 DL |
1240 | ;; Find a header and the column at which the command |
1241 | ;; name will be found. | |
4e1ede6c | 1242 | (while (re-search-forward "^key +binding\n\\(-+ +\\)-+\n\n" |
ff3453e4 DL |
1243 | nil t) |
1244 | (let ((col (- (match-end 1) (match-beginning 1)))) | |
1245 | (while | |
1246 | ;; Ignore single blank lines in table, but not | |
1247 | ;; double ones, which should terminate it. | |
95ac0a6f | 1248 | (and (not (looking-at "\n\\s-*\n")) |
ff3453e4 | 1249 | (progn |
657cca97 AS |
1250 | (and (eolp) (forward-line)) |
1251 | (end-of-line) | |
1252 | (skip-chars-backward "^\t\n") | |
1253 | (if (and (>= (current-column) col) | |
377d15d9 | 1254 | (looking-at "\\(\\sw\\|-\\)+$")) |
ff3453e4 DL |
1255 | (let ((sym (intern-soft (match-string 0)))) |
1256 | (if (fboundp sym) | |
4e1ede6c | 1257 | (help-xref-button |
d77dae5c DL |
1258 | 0 #'describe-function sym |
1259 | "mouse-2, RET: describe this function")))) | |
657cca97 | 1260 | (zerop (forward-line))))))))) |
400a1b1f | 1261 | (set-syntax-table stab)) |
a2e6f426 MB |
1262 | ;; Delete extraneous newlines at the end of the docstring |
1263 | (goto-char (point-max)) | |
1264 | (while (and (not (bobp)) (bolp)) | |
1265 | (delete-char -1)) | |
400a1b1f | 1266 | ;; Make a back-reference in this buffer if appropriate. |
376b2a24 | 1267 | (when (and help-xref-following help-xref-stack) |
400a1b1f RS |
1268 | (save-excursion |
1269 | (insert "\n\n" help-back-label)) | |
1270 | ;; Just to provide the match data: | |
1271 | (looking-at (concat "\n\n\\(" (regexp-quote help-back-label) "\\)")) | |
613a39b9 | 1272 | (help-xref-button 1 #'help-xref-go-back (current-buffer)))) |
400a1b1f RS |
1273 | ;; View mode steals RET from us. |
1274 | (set (make-local-variable 'minor-mode-overriding-map-alist) | |
1275 | (list (cons 'view-mode | |
1276 | (let ((map (make-sparse-keymap))) | |
ff3453e4 | 1277 | (set-keymap-parent map view-mode-map) |
400a1b1f RS |
1278 | (define-key map "\r" 'help-follow) |
1279 | map)))) | |
1280 | (set-buffer-modified-p old-modified)))) | |
1281 | ||
d77dae5c | 1282 | (defun help-xref-button (match-number function data &optional help-echo) |
400a1b1f RS |
1283 | "Make a hyperlink for cross-reference text previously matched. |
1284 | ||
1285 | MATCH-NUMBER is the subexpression of interest in the last matched | |
1286 | regexp. FUNCTION is a function to invoke when the button is | |
1287 | activated, applied to DATA. DATA may be a single value or a list. | |
d77dae5c DL |
1288 | See `help-make-xrefs'. |
1289 | If optional arg HELP-ECHO is supplied, it is used as a help string." | |
5f373960 RS |
1290 | ;; Don't mung properties we've added specially in some instances. |
1291 | (unless (get-text-property (match-beginning match-number) 'help-xref) | |
1292 | (add-text-properties (match-beginning match-number) | |
1293 | (match-end match-number) | |
1294 | (list 'mouse-face 'highlight | |
1295 | 'help-xref (cons function | |
1296 | (if (listp data) | |
1297 | data | |
1298 | (list data))))) | |
d77dae5c DL |
1299 | (if help-echo |
1300 | (put-text-property (match-beginning match-number) | |
1301 | (match-end match-number) | |
1302 | 'help-echo help-echo)) | |
5f373960 RS |
1303 | (if help-highlight-p |
1304 | (put-text-property (match-beginning match-number) | |
1305 | (match-end match-number) | |
1306 | 'face help-highlight-face)))) | |
400a1b1f | 1307 | |
96ede6b2 | 1308 | (defun help-insert-xref-button (string function data &optional help-echo) |
bb934822 | 1309 | "Insert STRING and make a hyperlink from cross-reference text on it. |
96ede6b2 KH |
1310 | |
1311 | FUNCTION is a function to invoke when the button is activated, applied | |
bb934822 | 1312 | to DATA. DATA may be a single value or a list. See `help-make-xrefs'. |
96ede6b2 KH |
1313 | If optional arg HELP-ECHO is supplied, it is used as a help string." |
1314 | (let ((pos (point))) | |
1315 | (insert string) | |
1316 | (goto-char pos) | |
1317 | (search-forward string) | |
1318 | (help-xref-button 0 function data help-echo))) | |
1319 | ||
1320 | ||
400a1b1f RS |
1321 | \f |
1322 | ;; Additional functions for (re-)creating types of help buffers. | |
1323 | (defun help-xref-interned (symbol) | |
1324 | "Follow a hyperlink which appeared to be an arbitrary interned SYMBOL. | |
1325 | ||
1326 | Both variable and function documentation are extracted into a single | |
1327 | help buffer." | |
950cf06f DL |
1328 | (let ((fdoc (when (fboundp symbol) (describe-function symbol))) |
1329 | (facedoc (when (facep symbol) (describe-face symbol)))) | |
accd1266 SM |
1330 | (when (or (boundp symbol) (not fdoc)) |
1331 | (describe-variable symbol) | |
1332 | ;; We now have a help buffer on the variable. Insert the function | |
1333 | ;; text before it. | |
950cf06f | 1334 | (when (or fdoc facedoc) |
accd1266 SM |
1335 | (with-current-buffer "*Help*" |
1336 | (goto-char (point-min)) | |
1337 | (let ((inhibit-read-only t)) | |
950cf06f DL |
1338 | (when fdoc |
1339 | (insert fdoc "\n\n")) | |
1340 | (when facedoc | |
1341 | (insert (make-string 30 ?-) "\n\n" (symbol-name symbol) | |
1342 | " is also a " "face." "\n\n" facedoc "\n\n")) | |
1343 | (insert (make-string 30 ?-) "\n\n" (symbol-name symbol) | |
1344 | " is also a " "variable." "\n\n")) | |
accd1266 | 1345 | (help-setup-xref (list #'help-xref-interned symbol) nil)))))) |
400a1b1f RS |
1346 | |
1347 | (defun help-xref-mode (buffer) | |
1348 | "Do a `describe-mode' for the specified BUFFER." | |
1349 | (save-excursion | |
1350 | (set-buffer buffer) | |
1351 | (describe-mode))) | |
1352 | \f | |
1353 | ;;; Navigation/hyperlinking with xrefs | |
1354 | ||
1355 | (defun help-follow-mouse (click) | |
1356 | "Follow the cross-reference that you click on." | |
1357 | (interactive "e") | |
9b49f910 RS |
1358 | (let* ((start (event-start click)) |
1359 | (window (car start)) | |
1360 | (pos (car (cdr start)))) | |
1361 | (with-current-buffer (window-buffer window) | |
400a1b1f RS |
1362 | (help-follow pos)))) |
1363 | ||
613a39b9 | 1364 | (defun help-xref-go-back (buffer) |
b2c85790 | 1365 | "From BUFFER, go back to previous help buffer text using `help-xref-stack'." |
4c45295b | 1366 | (let (item position method args) |
613a39b9 RS |
1367 | (with-current-buffer buffer |
1368 | (when help-xref-stack | |
1369 | (setq help-xref-stack (cdr help-xref-stack)) ; due to help-follow | |
376b2a24 | 1370 | (setq item (pop help-xref-stack) |
4c45295b KH |
1371 | position (car item) |
1372 | method (cadr item) | |
376b2a24 | 1373 | args (cddr item)))) |
4c45295b | 1374 | (apply method args) |
376b2a24 DL |
1375 | ;; We assume that the buffer we just recreated has the saved name, |
1376 | ;; which might not always be true. | |
1377 | (when (get-buffer (cdr position)) | |
1378 | (with-current-buffer (cdr position) | |
1379 | (goto-char (car position)))))) | |
400a1b1f RS |
1380 | |
1381 | (defun help-go-back () | |
b2c85790 | 1382 | "Invoke the [back] button (if any) in the Help mode buffer." |
400a1b1f RS |
1383 | (interactive) |
1384 | (help-follow (1- (point-max)))) | |
1385 | ||
400c12fd | 1386 | (defun help-follow (&optional pos) |
400a1b1f RS |
1387 | "Follow cross-reference at POS, defaulting to point. |
1388 | ||
1389 | For the cross-reference format, see `help-make-xrefs'." | |
1390 | (interactive "d") | |
400c12fd DL |
1391 | (unless pos |
1392 | (setq pos (point))) | |
accd1266 SM |
1393 | (let* ((help-data |
1394 | (or (and (not (= pos (point-max))) | |
1395 | (get-text-property pos 'help-xref)) | |
1396 | (and (not (= pos (point-min))) | |
1397 | (get-text-property (1- pos) 'help-xref)) | |
1398 | ;; check if the symbol under point is a function or variable | |
1399 | (let ((sym | |
1400 | (intern | |
1401 | (save-excursion | |
1402 | (goto-char pos) (skip-syntax-backward "w_") | |
1403 | (buffer-substring (point) | |
1404 | (progn (skip-syntax-forward "w_") | |
1405 | (point))))))) | |
1406 | (when (or (boundp sym) (fboundp sym)) | |
1407 | (list #'help-xref-interned sym))))) | |
400a1b1f RS |
1408 | (method (car help-data)) |
1409 | (args (cdr help-data))) | |
400a1b1f | 1410 | (when help-data |
376b2a24 DL |
1411 | (setq help-xref-stack (cons (cons (cons pos (buffer-name)) |
1412 | help-xref-stack-item) | |
accd1266 SM |
1413 | help-xref-stack)) |
1414 | (setq help-xref-stack-item nil) | |
400a1b1f | 1415 | ;; There is a reference at point. Follow it. |
376b2a24 DL |
1416 | (let ((help-xref-following t)) |
1417 | (apply method args))))) | |
400a1b1f RS |
1418 | |
1419 | ;; For tabbing through buffer. | |
1420 | (defun help-next-ref () | |
1421 | "Find the next help cross-reference in the buffer." | |
1422 | (interactive) | |
1423 | (let (pos) | |
1424 | (while (not pos) | |
1425 | (if (get-text-property (point) 'help-xref) ; move off reference | |
ff3453e4 DL |
1426 | (goto-char (or (next-single-property-change (point) 'help-xref) |
1427 | (point)))) | |
400a1b1f RS |
1428 | (cond ((setq pos (next-single-property-change (point) 'help-xref)) |
1429 | (if pos (goto-char pos))) | |
1430 | ((bobp) | |
1431 | (message "No cross references in the buffer.") | |
1432 | (setq pos t)) | |
1433 | (t ; be circular | |
1434 | (goto-char (point-min))))))) | |
1435 | ||
1436 | (defun help-previous-ref () | |
1437 | "Find the previous help cross-reference in the buffer." | |
1438 | (interactive) | |
1439 | (let (pos) | |
1440 | (while (not pos) | |
1441 | (if (get-text-property (point) 'help-xref) ; move off reference | |
1442 | (goto-char (or (previous-single-property-change (point) 'help-xref) | |
1443 | (point)))) | |
1444 | (cond ((setq pos (previous-single-property-change (point) 'help-xref)) | |
1445 | (if pos (goto-char pos))) | |
1446 | ((bobp) | |
1447 | (message "No cross references in the buffer.") | |
1448 | (setq pos t)) | |
1449 | (t ; be circular | |
1450 | (goto-char (point-max))))))) | |
1451 | ||
48ce3c22 RS |
1452 | \f |
1453 | ;;; Automatic resizing of temporary buffers. | |
1454 | ||
4483ddc5 | 1455 | (defcustom temp-buffer-max-height (lambda (buffer) (/ (- (frame-height) 2) 2)) |
48ce3c22 RS |
1456 | "*Maximum height of a window displaying a temporary buffer. |
1457 | This is the maximum height (in text lines) which `resize-temp-buffer-window' | |
1458 | will give to a window displaying a temporary buffer. | |
1459 | It can also be a function which will be called with the object corresponding | |
1460 | to the buffer to be displayed as argument and should return an integer | |
1461 | positive number." | |
1462 | :type '(choice integer function) | |
1463 | :group 'help | |
1464 | :version "20.4") | |
1465 | ||
4e1ede6c SM |
1466 | (define-minor-mode temp-buffer-resize-mode |
1467 | "Toggle the mode which makes windows smaller for temporary buffers. | |
48ce3c22 RS |
1468 | With prefix argument ARG, turn the resizing of windows displaying temporary |
1469 | buffers on if ARG is positive or off otherwise. | |
4e1ede6c SM |
1470 | This makes the window the right height for its contents, but never |
1471 | more than `temp-buffer-max-height' nor less than `window-min-height'. | |
1472 | This applies to `help', `apropos' and `completion' buffers, and some others." | |
1473 | nil nil nil :global t :group 'help | |
1474 | (if temp-buffer-resize-mode | |
57f43907 | 1475 | ;; `help-make-xrefs' may add a `back' button and thus increase the |
4e1ede6c SM |
1476 | ;; text size, so `resize-temp-buffer-window' must be run *after* it. |
1477 | (add-hook 'temp-buffer-show-hook 'resize-temp-buffer-window 'append) | |
8304a3bb | 1478 | (remove-hook 'temp-buffer-show-hook 'resize-temp-buffer-window))) |
48ce3c22 RS |
1479 | |
1480 | (defun resize-temp-buffer-window () | |
1481 | "Resize the current window to fit its contents. | |
4483ddc5 | 1482 | Will not make it higher than `temp-buffer-max-height' nor smaller than |
b2c85790 | 1483 | `window-min-height'. Do nothing if it is the only window on its frame, if it |
48ce3c22 RS |
1484 | is not as wide as the frame or if some of the window's contents are scrolled |
1485 | out of view." | |
1486 | (unless (or (one-window-p 'nomini) | |
1487 | (not (pos-visible-in-window-p (point-min))) | |
1488 | (/= (frame-width) (window-width))) | |
d9c30bdf MB |
1489 | (fit-window-to-buffer |
1490 | (selected-window) | |
1491 | (if (functionp temp-buffer-max-height) | |
1492 | (funcall temp-buffer-max-height (current-buffer)) | |
1493 | temp-buffer-max-height)))) | |
48ce3c22 | 1494 | |
f0d0fb19 DL |
1495 | ;; `help-manyarg-func-alist' is defined primitively (in doc.c). |
1496 | ;; New primitives with `MANY' or `UNEVALLED' arglists should be added | |
1497 | ;; to this alist. | |
1498 | ;; The parens and function name are redundant, but it's messy to add | |
1499 | ;; them in `documentation'. | |
3b4429b4 DL |
1500 | |
1501 | ;; This will find any missing items: | |
1502 | ;; (let (l) | |
1503 | ;; (mapatoms (lambda (x) | |
1504 | ;; (if (and (fboundp x) | |
1505 | ;; (subrp (symbol-function x)) | |
1506 | ;; (not (numberp (cdr (subr-arity (symbol-function x))))) | |
1507 | ;; (not (assq x help-manyarg-func-alist))) | |
1508 | ;; (push x l)))) | |
1509 | ;; l) | |
f0d0fb19 DL |
1510 | (defconst help-manyarg-func-alist |
1511 | (purecopy | |
1512 | '((list . "(list &rest OBJECTS)") | |
1513 | (vector . "(vector &rest OBJECTS)") | |
1514 | (make-byte-code . "(make-byte-code &rest ELEMENTS)") | |
1515 | (call-process | |
1516 | . "(call-process PROGRAM &optional INFILE BUFFER DISPLAY &rest ARGS)") | |
e3e36d74 DL |
1517 | (call-process-region |
1518 | . "(call-process-region START END PROGRAM &optional DELETE BUFFER DISPLAY &rest ARGS)") | |
f0d0fb19 DL |
1519 | (string . "(string &rest CHARACTERS)") |
1520 | (+ . "(+ &rest NUMBERS-OR-MARKERS)") | |
1521 | (- . "(- &optional NUMBER-OR-MARKER &rest MORE-NUMBERS-OR-MARKERS)") | |
1522 | (* . "(* &rest NUMBERS-OR-MARKERS)") | |
1523 | (/ . "(/ DIVIDEND DIVISOR &rest DIVISORS)") | |
1524 | (max . "(max NUMBER-OR-MARKER &rest NUMBERS-OR-MARKERS)") | |
1525 | (min . "(min NUMBER-OR-MARKER &rest NUMBERS-OR-MARKERS)") | |
1526 | (logand . "(logand &rest INTS-OR-MARKERS)") | |
1527 | (logior . "(logior &rest INTS-OR-MARKERS)") | |
1528 | (logxor . "(logxor &rest INTS-OR-MARKERS)") | |
1529 | (encode-time | |
1530 | . "(encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE)") | |
1531 | (insert . "(insert &rest ARGS)") | |
9a200f68 | 1532 | (insert-and-inherit . "(insert-and-inherit &rest ARGS)") |
f0d0fb19 DL |
1533 | (insert-before-markers . "(insert-before-markers &rest ARGS)") |
1534 | (message . "(message STRING &rest ARGUMENTS)") | |
1535 | (message-box . "(message-box STRING &rest ARGUMENTS)") | |
1536 | (message-or-box . "(message-or-box STRING &rest ARGUMENTS)") | |
1537 | (propertize . "(propertize STRING &rest PROPERTIES)") | |
1538 | (format . "(format STRING &rest OBJECTS)") | |
1539 | (apply . "(apply FUNCTION &rest ARGUMENTS)") | |
1540 | (run-hooks . "(run-hooks &rest HOOKS)") | |
2de47765 DL |
1541 | (run-hook-with-args . "(run-hook-with-args HOOK &rest ARGS)") |
1542 | (run-hook-with-args-until-failure | |
1543 | . "(run-hook-with-args-until-failure HOOK &rest ARGS)") | |
1544 | (run-hook-with-args-until-success | |
1545 | . "(run-hook-with-args-until-success HOOK &rest ARGS)") | |
f0d0fb19 DL |
1546 | (funcall . "(funcall FUNCTION &rest ARGUMENTS)") |
1547 | (append . "(append &rest SEQUENCES)") | |
1548 | (concat . "(concat &rest SEQUENCES)") | |
46c56972 | 1549 | (vconcat . "(vconcat &rest SEQUENCES)") |
f0d0fb19 DL |
1550 | (nconc . "(nconc &rest LISTS)") |
1551 | (widget-apply . "(widget-apply WIDGET PROPERTY &rest ARGS)") | |
1552 | (make-hash-table . "(make-hash-table &rest KEYWORD-ARGS)") | |
1553 | (insert-string . "(insert-string &rest ARGS)") | |
1554 | (start-process . "(start-process NAME BUFFER PROGRAM &rest PROGRAM-ARGS)") | |
1555 | (setq-default . "(setq-default SYMBOL VALUE [SYMBOL VALUE...])") | |
1556 | (save-excursion . "(save-excursion &rest BODY)") | |
1557 | (save-current-buffer . "(save-current-buffer &rest BODY)") | |
1558 | (save-restriction . "(save-restriction &rest BODY)") | |
1559 | (or . "(or CONDITIONS ...)") | |
1560 | (and . "(and CONDITIONS ...)") | |
1561 | (if . "(if COND THEN ELSE...)") | |
1562 | (cond . "(cond CLAUSES...)") | |
1563 | (progn . "(progn BODY ...)") | |
1564 | (prog1 . "(prog1 FIRST BODY...)") | |
1565 | (prog2 . "(prog2 X Y BODY...)") | |
1566 | (setq . "(setq SYM VAL SYM VAL ...)") | |
1567 | (quote . "(quote ARG)") | |
1568 | (function . "(function ARG)") | |
1569 | (defun . "(defun NAME ARGLIST [DOCSTRING] BODY...)") | |
1570 | (defmacro . "(defmacro NAME ARGLIST [DOCSTRING] BODY...)") | |
1571 | (defvar . "(defvar SYMBOL [INITVALUE DOCSTRING])") | |
1572 | (defconst . "(defconst SYMBOL INITVALUE [DOCSTRING])") | |
1573 | (let* . "(let* VARLIST BODY...)") | |
1574 | (let . "(let VARLIST BODY...)") | |
1575 | (while . "(while TEST BODY...)") | |
1576 | (catch . "(catch TAG BODY...)") | |
1577 | (unwind-protect . "(unwind-protect BODYFORM UNWINDFORMS...)") | |
1578 | (condition-case . "(condition-case VAR BODYFORM HANDLERS...)") | |
194959c7 | 1579 | (track-mouse . "(track-mouse BODY ...)") |
2de47765 DL |
1580 | (ml-if . "(ml-if COND THEN ELSE...)") |
1581 | (ml-provide-prefix-argument . "(ml-provide-prefix-argument ARG1 ARG2)") | |
3b4429b4 | 1582 | (ml-prefix-argument-loop . "(ml-prefix-argument-loop ...)") |
2de47765 DL |
1583 | (with-output-to-temp-buffer |
1584 | . "(with-output-to-temp-buffer BUFFNAME BODY ...)") | |
caf047ec DL |
1585 | (save-window-excursion . "(save-window-excursion BODY ...)") |
1586 | (find-operation-coding-system | |
3b4429b4 DL |
1587 | . "(find-operation-coding-system OPERATION ARGUMENTS ...)") |
1588 | (insert-before-markers-and-inherit | |
1589 | . "(insert-before-markers-and-inherit &rest ARGS)")))) | |
f0d0fb19 | 1590 | |
5fbeea74 GM |
1591 | ;; Provide this for the sake of define-minor-mode which generates |
1592 | ;; defcustoms which require 'help'. | |
1593 | ||
1594 | (provide 'help) | |
1595 | ||
1a06eabd | 1596 | ;;; help.el ends here |