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