Commit | Line | Data |
---|---|---|
1a06eabd ER |
1 | ;;; help.el --- help commands for Emacs |
2 | ||
400a1b1f | 3 | ;; Copyright (C) 1985, 1986, 1993, 1994, 1998 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 |
d9ecc911 ER |
28 | ;;`M-x help-for-help'. |
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)) |
41b8542b | 35 | |
433ae6f6 RS |
36 | (defvar help-map (make-sparse-keymap) |
37 | "Keymap for characters following the Help key.") | |
38 | ||
afaa65e4 KH |
39 | (defvar help-mode-map (make-sparse-keymap) |
40 | "Keymap for help mode.") | |
41 | ||
e17d2fd1 | 42 | (define-key global-map (char-to-string help-char) 'help-command) |
0af3df1c RS |
43 | (define-key global-map [help] 'help-command) |
44 | (define-key global-map [f1] 'help-command) | |
433ae6f6 RS |
45 | (fset 'help-command help-map) |
46 | ||
e17d2fd1 | 47 | (define-key help-map (char-to-string help-char) 'help-for-help) |
0af3df1c RS |
48 | (define-key help-map [help] 'help-for-help) |
49 | (define-key help-map [f1] 'help-for-help) | |
433ae6f6 RS |
50 | (define-key help-map "?" 'help-for-help) |
51 | ||
52 | (define-key help-map "\C-c" 'describe-copying) | |
53 | (define-key help-map "\C-d" 'describe-distribution) | |
54 | (define-key help-map "\C-w" 'describe-no-warranty) | |
76766f2d | 55 | (define-key help-map "\C-p" 'describe-project) |
122955bf | 56 | (define-key help-map "a" 'apropos-command) |
433ae6f6 RS |
57 | |
58 | (define-key help-map "b" 'describe-bindings) | |
59 | ||
60 | (define-key help-map "c" 'describe-key-briefly) | |
61 | (define-key help-map "k" 'describe-key) | |
62 | ||
63 | (define-key help-map "d" 'describe-function) | |
64 | (define-key help-map "f" 'describe-function) | |
65 | ||
7ee71cf1 RS |
66 | (define-key help-map "F" 'view-emacs-FAQ) |
67 | ||
433ae6f6 | 68 | (define-key help-map "i" 'info) |
e5d77022 JB |
69 | (define-key help-map "\C-f" 'Info-goto-emacs-command-node) |
70 | (define-key help-map "\C-k" 'Info-goto-emacs-key-command-node) | |
32884eab | 71 | (define-key help-map "\C-i" 'info-lookup-symbol) |
433ae6f6 RS |
72 | |
73 | (define-key help-map "l" 'view-lossage) | |
74 | ||
75 | (define-key help-map "m" 'describe-mode) | |
76 | ||
77 | (define-key help-map "\C-n" 'view-emacs-news) | |
78 | (define-key help-map "n" 'view-emacs-news) | |
79 | ||
06b98c51 | 80 | (define-key help-map "p" 'finder-by-keyword) |
3e9c095d RS |
81 | (autoload 'finder-by-keyword "finder" |
82 | "Find packages matching a given keyword." t) | |
06b98c51 | 83 | |
433ae6f6 RS |
84 | (define-key help-map "s" 'describe-syntax) |
85 | ||
86 | (define-key help-map "t" 'help-with-tutorial) | |
87 | ||
88 | (define-key help-map "w" 'where-is) | |
89 | ||
90 | (define-key help-map "v" 'describe-variable) | |
91 | ||
2fc9d9f4 RS |
92 | (define-key help-map "q" 'help-quit) |
93 | ||
400a1b1f RS |
94 | (define-key help-mode-map [mouse-2] 'help-follow-mouse) |
95 | (define-key help-mode-map "\C-c\C-b" 'help-go-back) | |
96 | (define-key help-mode-map "\C-c\C-c" 'help-follow) | |
97 | (define-key help-mode-map "\t" 'help-next-ref) | |
98 | (define-key help-mode-map [backtab] 'help-previous-ref) | |
306a5e68 | 99 | (define-key help-mode-map [(shift tab)] 'help-previous-ref) |
400a1b1f RS |
100 | ;; Documentation only, since we use minor-mode-overriding-map-alist. |
101 | (define-key help-mode-map "\r" 'help-follow) | |
102 | ||
103 | ;; Font-locking is incompatible with the new xref stuff. | |
104 | ;(defvar help-font-lock-keywords | |
105 | ; (eval-when-compile | |
106 | ; (let ((name-char "[-+a-zA-Z0-9_*]") (sym-char "[-+a-zA-Z0-9_:*]")) | |
107 | ; (list | |
108 | ; ;; | |
109 | ; ;; The symbol itself. | |
110 | ; (list (concat "\\`\\(" name-char "+\\)\\(\\(:\\)\\|\\('\\)\\)") | |
111 | ; '(1 (if (match-beginning 3) | |
112 | ; font-lock-function-name-face | |
113 | ; font-lock-variable-name-face))) | |
114 | ; ;; | |
115 | ; ;; Words inside `' which tend to be symbol names. | |
116 | ; (list (concat "`\\(" sym-char sym-char "+\\)'") | |
117 | ; 1 'font-lock-constant-face t) | |
118 | ; ;; | |
119 | ; ;; CLisp `:' keywords as references. | |
120 | ; (list (concat "\\<:" sym-char "+\\>") 0 'font-lock-builtin-face t)))) | |
121 | ; "Default expressions to highlight in Help mode.") | |
122 | ||
123 | (defvar help-xref-stack nil | |
124 | "A stack of ways by which to return to help buffers after following xrefs. | |
125 | Used by `help-follow' and `help-xref-go-back'.") | |
126 | (put 'help-xref-stack 'permanent-local t) | |
127 | ||
128 | (defvar help-xref-stack-item nil | |
129 | "An item for `help-follow' in this buffer to push onto `help-xref-stack'.") | |
130 | (put 'help-xref-stack-item 'permanent-local t) | |
131 | ||
132 | (setq-default help-xref-stack nil help-xref-stack-item nil) | |
507fb916 | 133 | |
afaa65e4 | 134 | (defun help-mode () |
400a1b1f | 135 | "Major mode for viewing help text and navigating references in it. |
afaa65e4 KH |
136 | Entry to this mode runs the normal hook `help-mode-hook'. |
137 | Commands: | |
138 | \\{help-mode-map}" | |
139 | (interactive) | |
140 | (kill-all-local-variables) | |
141 | (use-local-map help-mode-map) | |
142 | (setq mode-name "Help") | |
143 | (setq major-mode 'help-mode) | |
507fb916 | 144 | (make-local-variable 'font-lock-defaults) |
400a1b1f | 145 | (setq font-lock-defaults nil) ; font-lock would defeat xref |
42499979 | 146 | (view-mode) |
f90b6922 RS |
147 | (make-local-variable 'view-no-disable-on-exit) |
148 | (setq view-no-disable-on-exit t) | |
400a1b1f RS |
149 | ;; `help-make-xrefs' would be run here if not invoked from |
150 | ;; `help-mode-maybe'. | |
afaa65e4 KH |
151 | (run-hooks 'help-mode-hook)) |
152 | ||
21de5941 KH |
153 | (defun help-mode-maybe () |
154 | (if (eq major-mode 'fundamental-mode) | |
01364a75 | 155 | (help-mode)) |
400a1b1f RS |
156 | (when (eq major-mode 'help-mode) |
157 | ;; View mode's read-only status of existing *Help* buffer is lost | |
158 | ;; by with-output-to-temp-buffer. | |
159 | (toggle-read-only 1) | |
160 | (help-make-xrefs (current-buffer))) | |
01364a75 RS |
161 | (setq view-return-to-alist |
162 | (list (cons (selected-window) help-return-method)))) | |
21de5941 KH |
163 | |
164 | (add-hook 'temp-buffer-show-hook 'help-mode-maybe) | |
165 | ||
2fc9d9f4 RS |
166 | (defun help-quit () |
167 | (interactive) | |
168 | nil) | |
169 | ||
0634ea78 KH |
170 | (defun help-with-tutorial (&optional arg) |
171 | "Select the Emacs learn-by-doing tutorial. | |
da412772 | 172 | If there is a tutorial version written in the language |
71e9bd71 | 173 | of the selected language environment, that version is used. |
da412772 | 174 | If there's no tutorial in that language, `TUTORIAL' is selected. |
c822b44b | 175 | With arg, you are asked to choose which language." |
0634ea78 | 176 | (interactive "P") |
3060bf83 KH |
177 | (let ((lang (if arg |
178 | (read-language-name 'tutorial "Language: " "English") | |
179 | (if (get-language-info current-language-environment 'tutorial) | |
180 | current-language-environment | |
ad21fa07 RS |
181 | "English"))) |
182 | file filename) | |
3060bf83 | 183 | (setq filename (get-language-info lang 'tutorial)) |
7c9b148e | 184 | (setq file (expand-file-name (concat "~/" filename))) |
433ae6f6 RS |
185 | (delete-other-windows) |
186 | (if (get-file-buffer file) | |
187 | (switch-to-buffer (get-file-buffer file)) | |
188 | (switch-to-buffer (create-file-buffer file)) | |
189 | (setq buffer-file-name file) | |
190 | (setq default-directory (expand-file-name "~/")) | |
79058860 | 191 | (setq buffer-auto-save-file-name nil) |
0634ea78 | 192 | (insert-file-contents (expand-file-name filename data-directory)) |
433ae6f6 RS |
193 | (goto-char (point-min)) |
194 | (search-forward "\n<<") | |
195 | (beginning-of-line) | |
196 | (delete-region (point) (progn (end-of-line) (point))) | |
857a1de6 | 197 | (let ((n (- (window-height (selected-window)) |
433ae6f6 | 198 | (count-lines (point-min) (point)) |
857a1de6 | 199 | 6))) |
d0da2301 | 200 | (if (< n 12) |
857a1de6 KH |
201 | (newline n) |
202 | ;; Some people get confused by the large gap. | |
203 | (newline (/ n 2)) | |
204 | (insert "[Middle of page left blank for didactic purposes. " | |
205 | "Text continues below]") | |
206 | (newline (- n (/ n 2))))) | |
433ae6f6 RS |
207 | (goto-char (point-min)) |
208 | (set-buffer-modified-p nil)))) | |
209 | ||
e88a2c59 RS |
210 | (defun describe-key-briefly (key &optional insert) |
211 | "Print the name of the function KEY invokes. KEY is a string. | |
212 | If INSERT (the prefix arg) is non-nil, insert the message in the buffer." | |
213 | (interactive "kDescribe key briefly: \nP") | |
5f296b78 RS |
214 | ;; If this key seq ends with a down event, discard the |
215 | ;; following click or drag event. Otherwise that would | |
216 | ;; erase the message. | |
217 | (let ((type (aref key (1- (length key))))) | |
218 | (if (listp type) (setq type (car type))) | |
219 | (and (symbolp type) | |
220 | (memq 'down (event-modifiers type)) | |
fca4b775 | 221 | (read-event))) |
fc558e4d RS |
222 | (save-excursion |
223 | (let ((modifiers (event-modifiers (aref key 0))) | |
e88a2c59 | 224 | (standard-output (if insert (current-buffer) t)) |
fc558e4d RS |
225 | window position) |
226 | ;; For a mouse button event, go to the button it applies to | |
227 | ;; to get the right key bindings. And go to the right place | |
228 | ;; in case the keymap depends on where you clicked. | |
229 | (if (or (memq 'click modifiers) (memq 'down modifiers) | |
230 | (memq 'drag modifiers)) | |
231 | (setq window (posn-window (event-start (aref key 0))) | |
232 | position (posn-point (event-start (aref key 0))))) | |
233 | (if (windowp window) | |
234 | (progn | |
235 | (set-buffer (window-buffer window)) | |
236 | (goto-char position))) | |
237 | ;; Ok, now look up the key and name the command. | |
e88a2c59 RS |
238 | (let ((defn (key-binding key)) |
239 | (key-desc (key-description key))) | |
fc558e4d | 240 | (if (or (null defn) (integerp defn)) |
e88a2c59 RS |
241 | (princ (format "%s is undefined" key-desc)) |
242 | (princ (format (if insert | |
0f2aa0e1 | 243 | "`%s' (`%s')" |
e88a2c59 RS |
244 | (if (windowp window) |
245 | "%s at that spot runs the command %s" | |
246 | "%s runs the command %s")) | |
247 | key-desc | |
248 | (if (symbolp defn) defn (prin1-to-string defn))))))))) | |
433ae6f6 | 249 | |
01364a75 RS |
250 | (defvar help-return-method nil |
251 | "What to do to \"exit\" the help buffer. | |
252 | This is a list | |
253 | (WINDOW . t) delete the selected window, go to WINDOW. | |
254 | (WINDOW . quit-window) do quit-window, then select WINDOW. | |
255 | (WINDOW BUF START POINT) display BUF at START, POINT, then select WINDOW.") | |
256 | ||
433ae6f6 RS |
257 | (defun print-help-return-message (&optional function) |
258 | "Display or return message saying how to restore windows after help command. | |
259 | Computes a message and applies the optional argument FUNCTION to it. | |
260 | If FUNCTION is nil, applies `message' to it, thus printing it." | |
261 | (and (not (get-buffer-window standard-output)) | |
d536293f | 262 | (let ((first-message |
a1c9f209 | 263 | (cond ((special-display-p (buffer-name standard-output)) |
01364a75 | 264 | (setq help-return-method (cons (selected-window) t)) |
d536293f RS |
265 | ;; If the help output buffer is a special display buffer, |
266 | ;; don't say anything about how to get rid of it. | |
267 | ;; First of all, the user will do that with the window | |
268 | ;; manager, not with Emacs. | |
269 | ;; Secondly, the buffer has not been displayed yet, | |
270 | ;; so we don't know whether its frame will be selected. | |
d536293f RS |
271 | nil) |
272 | ((not (one-window-p t)) | |
01364a75 RS |
273 | (setq help-return-method |
274 | (cons (selected-window) 'quit-window)) | |
d536293f RS |
275 | "Type \\[switch-to-buffer-other-window] RET to restore the other window.") |
276 | (pop-up-windows | |
01364a75 | 277 | (setq help-return-method (cons (selected-window) t)) |
d536293f RS |
278 | "Type \\[delete-other-windows] to remove help window.") |
279 | (t | |
01364a75 RS |
280 | (setq help-return-method |
281 | (list (selected-window) (window-buffer) | |
282 | (window-start) (window-point))) | |
d536293f RS |
283 | "Type \\[switch-to-buffer] RET to remove help window.")))) |
284 | (funcall (or function 'message) | |
285 | (concat | |
286 | (if first-message | |
287 | (substitute-command-keys first-message) | |
288 | "") | |
289 | (if first-message " " "") | |
125a8d70 RS |
290 | ;; If the help buffer will go in a separate frame, |
291 | ;; it's no use mentioning a command to scroll, so don't. | |
a1c9f209 | 292 | (if (special-display-p (buffer-name standard-output)) |
125a8d70 | 293 | nil |
a1c9f209 | 294 | (if (same-window-p (buffer-name standard-output)) |
125a8d70 RS |
295 | ;; Say how to scroll this window. |
296 | (substitute-command-keys | |
297 | "\\[scroll-up] to scroll the help.") | |
298 | ;; Say how to scroll some other window. | |
6e7f5182 | 299 | (substitute-command-keys |
125a8d70 | 300 | "\\[scroll-other-window] to scroll the help.")))))))) |
433ae6f6 RS |
301 | |
302 | (defun describe-key (key) | |
303 | "Display documentation of the function invoked by KEY. KEY is a string." | |
304 | (interactive "kDescribe key: ") | |
5f296b78 RS |
305 | ;; If this key seq ends with a down event, discard the |
306 | ;; following click or drag event. Otherwise that would | |
307 | ;; erase the message. | |
308 | (let ((type (aref key (1- (length key))))) | |
309 | (if (listp type) (setq type (car type))) | |
310 | (and (symbolp type) | |
311 | (memq 'down (event-modifiers type)) | |
312 | (read-event))) | |
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))) | |
327 | (let ((defn (key-binding key))) | |
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 KH |
336 | (princ "\n which is ") |
337 | (describe-function-1 defn nil) | |
fc558e4d | 338 | (print-help-return-message))))))) |
433ae6f6 | 339 | |
ad023904 RS |
340 | (defun describe-mode () |
341 | "Display documentation of current major mode and minor modes. | |
433ae6f6 | 342 | For this to work correctly for a minor mode, the mode's indicator variable |
61c6b658 | 343 | \(listed in `minor-mode-alist') must also be a function whose documentation |
433ae6f6 | 344 | describes the minor mode." |
7192540b | 345 | (interactive) |
433ae6f6 | 346 | (with-output-to-temp-buffer "*Help*" |
7192540b | 347 | (let ((minor-modes minor-mode-alist) |
ddbe99e0 | 348 | (first t)) |
7192540b RS |
349 | (while minor-modes |
350 | (let* ((minor-mode (car (car minor-modes))) | |
ddbe99e0 | 351 | (indicator (car (cdr (car minor-modes))))) |
7192540b RS |
352 | ;; Document a minor mode if it is listed in minor-mode-alist, |
353 | ;; bound locally in this buffer, non-nil, and has a function | |
354 | ;; definition. | |
ddbe99e0 | 355 | (if (and (symbol-value minor-mode) |
7192540b RS |
356 | (fboundp minor-mode)) |
357 | (let ((pretty-minor-mode minor-mode)) | |
358 | (if (string-match "-mode$" (symbol-name minor-mode)) | |
359 | (setq pretty-minor-mode | |
360 | (capitalize | |
361 | (substring (symbol-name minor-mode) | |
362 | 0 (match-beginning 0))))) | |
363 | (while (and indicator (symbolp indicator)) | |
364 | (setq indicator (symbol-value indicator))) | |
83f86594 RS |
365 | (if first |
366 | (princ "The minor modes are described first, | |
367 | followed by the major mode, which is described on the last page.\n\f\n")) | |
368 | (setq first nil) | |
2ef581f3 RS |
369 | (princ (format "%s minor mode (%s):\n" |
370 | pretty-minor-mode | |
371 | (if indicator | |
372 | (format "indicator%s" indicator) | |
373 | "no indicator"))) | |
7192540b | 374 | (princ (documentation minor-mode)) |
83f86594 | 375 | (princ "\n\f\n")))) |
7192540b | 376 | (setq minor-modes (cdr minor-modes)))) |
433ae6f6 | 377 | (princ mode-name) |
ad023904 | 378 | (princ " mode:\n") |
433ae6f6 | 379 | (princ (documentation major-mode)) |
400a1b1f | 380 | (help-setup-xref (cons #'help-xref-mode (current-buffer)) (interactive-p)) |
433ae6f6 RS |
381 | (print-help-return-message))) |
382 | ||
383 | ;; So keyboard macro definitions are documented correctly | |
384 | (fset 'defining-kbd-macro (symbol-function 'start-kbd-macro)) | |
385 | ||
386 | (defun describe-distribution () | |
387 | "Display info on how to obtain the latest version of GNU Emacs." | |
388 | (interactive) | |
389 | (find-file-read-only | |
1e6dacf6 | 390 | (expand-file-name "DISTRIB" data-directory))) |
433ae6f6 RS |
391 | |
392 | (defun describe-copying () | |
393 | "Display info on how you may redistribute copies of GNU Emacs." | |
394 | (interactive) | |
395 | (find-file-read-only | |
1e6dacf6 | 396 | (expand-file-name "COPYING" data-directory)) |
433ae6f6 RS |
397 | (goto-char (point-min))) |
398 | ||
76766f2d RS |
399 | (defun describe-project () |
400 | "Display info on the GNU project." | |
401 | (interactive) | |
402 | (find-file-read-only | |
403 | (expand-file-name "GNU" data-directory)) | |
404 | (goto-char (point-min))) | |
405 | ||
433ae6f6 RS |
406 | (defun describe-no-warranty () |
407 | "Display info on all the kinds of warranty Emacs does NOT have." | |
408 | (interactive) | |
409 | (describe-copying) | |
410 | (let (case-fold-search) | |
411 | (search-forward "NO WARRANTY") | |
412 | (recenter 0))) | |
413 | ||
61c6b658 | 414 | (defun describe-prefix-bindings () |
c7cba9cb RS |
415 | "Describe the bindings of the prefix used to reach this command. |
416 | The prefix described consists of all but the last event | |
417 | of the key sequence that ran this command." | |
61c6b658 | 418 | (interactive) |
ccc06dcc KH |
419 | (let* ((key (this-command-keys))) |
420 | (describe-bindings | |
421 | (if (stringp key) | |
422 | (substring key 0 (1- (length key))) | |
423 | (let ((prefix (make-vector (1- (length key)) nil)) | |
424 | (i 0)) | |
425 | (while (< i (length prefix)) | |
426 | (aset prefix i (aref key i)) | |
427 | (setq i (1+ i))) | |
428 | prefix))))) | |
c7cba9cb RS |
429 | ;; Make C-h after a prefix, when not specifically bound, |
430 | ;; run describe-prefix-bindings. | |
61c6b658 RS |
431 | (setq prefix-help-command 'describe-prefix-bindings) |
432 | ||
382d018a RS |
433 | (defun view-emacs-news (&optional arg) |
434 | "Display info on recent changes to Emacs. | |
435 | With numeric argument display information on correspondingly older changes." | |
436 | (interactive "P") | |
437 | (let* ((arg (if arg (prefix-numeric-value arg) 0))) | |
438 | (find-file-read-only | |
439 | (expand-file-name (concat (make-string arg ?O) "NEWS") | |
440 | data-directory)))) | |
433ae6f6 | 441 | |
7ee71cf1 RS |
442 | (defun view-emacs-FAQ () |
443 | "Display the Emacs Frequently Asked Questions (FAQ) file." | |
444 | (interactive) | |
445 | (find-file-read-only (expand-file-name "FAQ" data-directory))) | |
446 | ||
433ae6f6 RS |
447 | (defun view-lossage () |
448 | "Display last 100 input keystrokes." | |
449 | (interactive) | |
450 | (with-output-to-temp-buffer "*Help*" | |
298a7c8c RS |
451 | (princ (mapconcat (function (lambda (key) |
452 | (if (or (integerp key) | |
453 | (symbolp key) | |
454 | (listp key)) | |
455 | (single-key-description key) | |
456 | (prin1-to-string key nil)))) | |
457 | (recent-keys) | |
458 | " ")) | |
433ae6f6 RS |
459 | (save-excursion |
460 | (set-buffer standard-output) | |
461 | (goto-char (point-min)) | |
462 | (while (progn (move-to-column 50) (not (eobp))) | |
463 | (search-forward " " nil t) | |
613a39b9 RS |
464 | (insert "\n")) |
465 | (setq help-xref-stack nil | |
466 | help-xref-stack-item nil)) | |
433ae6f6 RS |
467 | (print-help-return-message))) |
468 | ||
2fc9d9f4 | 469 | (defalias 'help 'help-for-help) |
41b8542b | 470 | (make-help-screen help-for-help |
a30a106b | 471 | "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:" |
76766f2d | 472 | "You have typed \\[help-command], the help character. Type a Help option: |
efcce2d2 | 473 | \(Use SPC or DEL to scroll through this text. Type \\<help-map>\\[help-quit] to exit the Help command.) |
433ae6f6 | 474 | |
21ee8c42 RM |
475 | a command-apropos. Give a substring, and see a list of commands |
476 | (functions interactively callable) that contain | |
477 | that substring. See also the apropos command. | |
af6a9de9 RS |
478 | b describe-bindings. Display table of all key bindings. |
479 | c describe-key-briefly. Type a command key sequence; | |
21ee8c42 | 480 | it prints the function name that sequence runs. |
a30a106b RS |
481 | C describe-coding-system. This describes either a specific coding system |
482 | (if you type its name) or the coding systems currently in use | |
483 | (if you type just RET). | |
af6a9de9 | 484 | f describe-function. Type a function name and get documentation of it. |
21ee8c42 RM |
485 | C-f Info-goto-emacs-command-node. Type a function name; |
486 | it takes you to the Info node for that command. | |
af6a9de9 | 487 | i info. The info documentation reader. |
a30a106b RS |
488 | I describe-input-method. Describe a specific input method (if you type |
489 | its name) or the current input method (if you type just RET). | |
af6a9de9 | 490 | k describe-key. Type a command key sequence; |
21ee8c42 RM |
491 | it displays the full documentation. |
492 | C-k Info-goto-emacs-key-command-node. Type a command key sequence; | |
493 | it takes you to the Info node for the command bound to that key. | |
af6a9de9 | 494 | l view-lossage. Shows last 100 characters you typed. |
a30a106b RS |
495 | L describe-language-environment. This describes either the a |
496 | specific language environment (if you type its name) | |
497 | or the current language environment (if you type just RET). | |
ed13681f KH |
498 | m describe-mode. Print documentation of current minor modes, |
499 | and the current major mode, including their special commands. | |
af6a9de9 RS |
500 | n view-emacs-news. Shows emacs news file. |
501 | p finder-by-keyword. Find packages matching a given topic keyword. | |
502 | s describe-syntax. Display contents of syntax table, plus explanations | |
503 | t help-with-tutorial. Select the Emacs learn-by-doing tutorial. | |
504 | v describe-variable. Type name of a variable; | |
21ee8c42 | 505 | it displays the variable's documentation and value. |
af6a9de9 | 506 | w where-is. Type command name; it prints which keystrokes |
21ee8c42 | 507 | invoke that command. |
a30a106b RS |
508 | |
509 | F Display the frequently asked questions file. | |
510 | h Display the HELLO file which illustrates various scripts. | |
511 | C-c Display Emacs copying permission (General Public License). | |
512 | C-d Display Emacs ordering information. | |
513 | C-n Display news of recent Emacs changes. | |
514 | C-p Display information about the GNU project. | |
515 | C-w Display information on absence of warranty for GNU Emacs." | |
41b8542b | 516 | help-map) |
433ae6f6 RS |
517 | |
518 | ;; Return a function which is called by the list containing point. | |
519 | ;; If that gives no function, return a function whose name is around point. | |
520 | ;; If that doesn't give a function, return nil. | |
521 | (defun function-called-at-point () | |
11267867 KH |
522 | (let ((stab (syntax-table))) |
523 | (set-syntax-table emacs-lisp-mode-syntax-table) | |
524 | (unwind-protect | |
525 | (or (condition-case () | |
526 | (save-excursion | |
527 | (save-restriction | |
528 | (narrow-to-region (max (point-min) (- (point) 1000)) (point-max)) | |
529 | ;; Move up to surrounding paren, then after the open. | |
530 | (backward-up-list 1) | |
531 | (forward-char 1) | |
532 | ;; If there is space here, this is probably something | |
533 | ;; other than a real Lisp function call, so ignore it. | |
534 | (if (looking-at "[ \t]") | |
535 | (error "Probably not a Lisp function call")) | |
536 | (let (obj) | |
537 | (setq obj (read (current-buffer))) | |
538 | (and (symbolp obj) (fboundp obj) obj)))) | |
539 | (error nil)) | |
540 | (condition-case () | |
914a48d0 | 541 | (save-excursion |
914a48d0 RS |
542 | (or (not (zerop (skip-syntax-backward "_w"))) |
543 | (eq (char-syntax (following-char)) ?w) | |
544 | (eq (char-syntax (following-char)) ?_) | |
545 | (forward-sexp -1)) | |
546 | (skip-chars-forward "'") | |
547 | (let ((obj (read (current-buffer)))) | |
548 | (and (symbolp obj) (fboundp obj) obj))) | |
11267867 KH |
549 | (error nil))) |
550 | (set-syntax-table stab)))) | |
433ae6f6 | 551 | |
ca5ed196 RS |
552 | (defun describe-function-find-file (function) |
553 | (let ((files load-history) | |
554 | file functions) | |
555 | (while files | |
556 | (if (memq function (cdr (car files))) | |
557 | (setq file (car (car files)) files nil)) | |
558 | (setq files (cdr files))) | |
559 | file)) | |
560 | ||
433ae6f6 RS |
561 | (defun describe-function (function) |
562 | "Display the full documentation of FUNCTION (a symbol)." | |
563 | (interactive | |
564 | (let ((fn (function-called-at-point)) | |
565 | (enable-recursive-minibuffers t) | |
566 | val) | |
567 | (setq val (completing-read (if fn | |
568 | (format "Describe function (default %s): " fn) | |
569 | "Describe function: ") | |
1bacc93e | 570 | obarray 'fboundp t nil nil (symbol-name fn))) |
433ae6f6 RS |
571 | (list (if (equal val "") |
572 | fn (intern val))))) | |
00d3de8e RS |
573 | (if function |
574 | (with-output-to-temp-buffer "*Help*" | |
575 | (prin1 function) | |
eea844b2 RS |
576 | ;; Use " is " instead of a colon so that |
577 | ;; it is easier to get out the function name using forward-sexp. | |
578 | (princ " is ") | |
05f6170c | 579 | (describe-function-1 function nil) |
00d3de8e RS |
580 | (print-help-return-message) |
581 | (save-excursion | |
582 | (set-buffer standard-output) | |
00d3de8e RS |
583 | ;; Return the text we displayed. |
584 | (buffer-string))) | |
585 | (message "You didn't specify a function"))) | |
586 | ||
05f6170c KH |
587 | (defun describe-function-1 (function parens) |
588 | (let* ((def (symbol-function function)) | |
589 | file-name string need-close | |
590 | (beg (if (commandp def) "an interactive " "a "))) | |
591 | (setq string | |
592 | (cond ((or (stringp def) | |
593 | (vectorp def)) | |
594 | "a keyboard macro") | |
595 | ((subrp def) | |
596 | (concat beg "built-in function")) | |
597 | ((byte-code-function-p def) | |
598 | (concat beg "compiled Lisp function")) | |
599 | ((symbolp def) | |
600 | (format "alias for `%s'" def)) | |
601 | ((eq (car-safe def) 'lambda) | |
602 | (concat beg "Lisp function")) | |
603 | ((eq (car-safe def) 'macro) | |
604 | "a Lisp macro") | |
605 | ((eq (car-safe def) 'mocklisp) | |
606 | "a mocklisp function") | |
607 | ((eq (car-safe def) 'autoload) | |
608 | (setq file-name (nth 1 def)) | |
609 | (format "%s autoloaded Lisp %s" | |
610 | (if (commandp def) "an interactive" "an") | |
611 | (if (nth 4 def) "macro" "function") | |
612 | )) | |
613 | (t ""))) | |
614 | (when (and parens (not (equal string ""))) | |
615 | (setq need-close t) | |
616 | (princ "(")) | |
617 | (princ string) | |
618 | (or file-name | |
619 | (setq file-name (describe-function-find-file function))) | |
620 | (if file-name | |
621 | (progn | |
622 | (princ " in `") | |
623 | ;; We used to add .el to the file name, | |
624 | ;; but that's completely wrong when the user used load-file. | |
625 | (princ file-name) | |
2676e099 DL |
626 | (princ "'") |
627 | ;; Make a hyperlink to the library. | |
628 | (with-current-buffer "*Help*" | |
629 | (save-excursion | |
630 | (re-search-backward "`\\([^`']+\\)'" nil t) | |
7dcf1127 RS |
631 | (help-xref-button 1 #'(lambda (arg) |
632 | (let ((location | |
633 | (find-function-noselect arg))) | |
634 | (display-buffer (nth 0 location)) | |
635 | (goto-char (nth 1 location)))) | |
636 | function))))) | |
05f6170c KH |
637 | (if need-close (princ ")")) |
638 | (princ ".") | |
639 | (terpri) | |
640 | (let* ((inner-function (if (and (listp def) 'macro) | |
641 | (cdr def) | |
642 | def)) | |
643 | (arglist (cond ((byte-code-function-p inner-function) | |
644 | (car (append inner-function nil))) | |
645 | ((eq (car-safe inner-function) 'lambda) | |
646 | (nth 1 inner-function)) | |
647 | (t t)))) | |
648 | (if (listp arglist) | |
649 | (progn | |
650 | (princ (cons function | |
651 | (mapcar (lambda (arg) | |
652 | (if (memq arg '(&optional &rest)) | |
653 | arg | |
654 | (intern (upcase (symbol-name arg))))) | |
655 | arglist))) | |
656 | (terpri)))) | |
657 | (let ((doc (documentation function))) | |
658 | (if doc | |
659 | (progn (terpri) | |
660 | (princ doc) | |
661 | (help-setup-xref (cons #'describe-function function) (interactive-p))) | |
662 | (princ "not documented"))))) | |
663 | ||
00d3de8e | 664 | ;; We return 0 if we can't find a variable to return. |
433ae6f6 RS |
665 | (defun variable-at-point () |
666 | (condition-case () | |
914a48d0 RS |
667 | (let ((stab (syntax-table))) |
668 | (unwind-protect | |
669 | (save-excursion | |
670 | (set-syntax-table emacs-lisp-mode-syntax-table) | |
671 | (or (not (zerop (skip-syntax-backward "_w"))) | |
672 | (eq (char-syntax (following-char)) ?w) | |
673 | (eq (char-syntax (following-char)) ?_) | |
674 | (forward-sexp -1)) | |
675 | (skip-chars-forward "'") | |
676 | (let ((obj (read (current-buffer)))) | |
00d3de8e RS |
677 | (or (and (symbolp obj) (boundp obj) obj) |
678 | 0))) | |
914a48d0 | 679 | (set-syntax-table stab))) |
00d3de8e | 680 | (error 0))) |
433ae6f6 RS |
681 | |
682 | (defun describe-variable (variable) | |
683 | "Display the full documentation of VARIABLE (a symbol). | |
684 | Returns the documentation as a string, also." | |
685 | (interactive | |
686 | (let ((v (variable-at-point)) | |
687 | (enable-recursive-minibuffers t) | |
688 | val) | |
00d3de8e | 689 | (setq val (completing-read (if (symbolp v) |
433ae6f6 RS |
690 | (format "Describe variable (default %s): " v) |
691 | "Describe variable: ") | |
d5645846 KH |
692 | obarray 'boundp t nil nil |
693 | (if (symbolp v) (symbol-name v)))) | |
433ae6f6 RS |
694 | (list (if (equal val "") |
695 | v (intern val))))) | |
00d3de8e | 696 | (if (symbolp variable) |
9a656d19 RS |
697 | (let (valvoid) |
698 | (with-output-to-temp-buffer "*Help*" | |
699 | (prin1 variable) | |
700 | (if (not (boundp variable)) | |
701 | (progn | |
702 | (princ " is void") | |
703 | (terpri) | |
704 | (setq valvoid t)) | |
705 | (princ "'s value is ") | |
706 | (terpri) | |
707 | (pp (symbol-value variable)) | |
708 | (terpri)) | |
709 | (if (local-variable-p variable) | |
710 | (progn | |
711 | (princ (format "Local in buffer %s; " (buffer-name))) | |
712 | (if (not (default-boundp variable)) | |
713 | (princ "globally void") | |
714 | (princ "global value is ") | |
715 | (terpri) | |
716 | (pp (default-value variable))) | |
717 | (terpri))) | |
718 | (terpri) | |
719 | (save-current-buffer | |
720 | (set-buffer standard-output) | |
721 | (if (> (count-lines (point-min) (point-max)) 10) | |
722 | (progn | |
723 | (goto-char (point-min)) | |
724 | (if valvoid | |
725 | (forward-line 1) | |
726 | (forward-sexp 1) | |
727 | (delete-region (point) (progn (end-of-line) (point))) | |
728 | (insert "'s value is shown below.\n\n") | |
729 | (save-excursion | |
730 | (insert "\n\nValue:")))))) | |
731 | (princ "Documentation:") | |
732 | (terpri) | |
733 | (let ((doc (documentation-property variable 'variable-documentation))) | |
734 | (princ (or doc "not documented as a variable."))) | |
400a1b1f | 735 | (help-setup-xref (cons #'describe-variable variable) (interactive-p)) |
7e824765 RS |
736 | |
737 | ;; Make a link to customize if this variable can be customized. | |
738 | (if (or (get variable 'custom-type) | |
739 | (user-variable-p variable)) | |
740 | (let ((customize-label "customize")) | |
741 | (terpri) | |
742 | (terpri) | |
743 | (princ (concat "You can " customize-label " this variable.")) | |
744 | (with-current-buffer "*Help*" | |
745 | (save-excursion | |
746 | (re-search-backward | |
747 | (concat "\\(" customize-label "\\)") nil t) | |
748 | (help-xref-button 1 #'(lambda (v) | |
749 | (customize-variable v)) variable) | |
750 | )))) | |
751 | ||
9a656d19 RS |
752 | (print-help-return-message) |
753 | (save-excursion | |
754 | (set-buffer standard-output) | |
9a656d19 RS |
755 | ;; Return the text we displayed. |
756 | (buffer-string)))) | |
00d3de8e | 757 | (message "You did not specify a variable"))) |
433ae6f6 | 758 | |
a8ad43aa RS |
759 | (defun describe-bindings (&optional prefix) |
760 | "Show a list of all defined keys, and their definitions. | |
761 | We put that list in a buffer, and display the buffer. | |
762 | ||
763 | The optional argument PREFIX, if non-nil, should be a key sequence; | |
764 | then we display only bindings that start with that prefix." | |
a249d3a0 | 765 | (interactive "P") |
613a39b9 RS |
766 | (describe-bindings-internal nil prefix) |
767 | (with-current-buffer "*Help*" | |
768 | (setq help-xref-stack nil | |
769 | help-xref-stack-item nil))) | |
a8ad43aa | 770 | |
e88a2c59 | 771 | (defun where-is (definition &optional insert) |
54c0b967 | 772 | "Print message listing key sequences that invoke specified command. |
e88a2c59 RS |
773 | Argument is a command definition, usually a symbol with a function definition. |
774 | If INSERT (the prefix arg) is non-nil, insert the message in the buffer." | |
54c0b967 RS |
775 | (interactive |
776 | (let ((fn (function-called-at-point)) | |
777 | (enable-recursive-minibuffers t) | |
778 | val) | |
779 | (setq val (completing-read (if fn | |
780 | (format "Where is command (default %s): " fn) | |
781 | "Where is command: ") | |
782 | obarray 'fboundp t)) | |
783 | (list (if (equal val "") | |
e88a2c59 RS |
784 | fn (intern val)) |
785 | current-prefix-arg))) | |
54c0b967 | 786 | (let* ((keys (where-is-internal definition overriding-local-map nil nil)) |
e88a2c59 RS |
787 | (keys1 (mapconcat 'key-description keys ", ")) |
788 | (standard-output (if insert (current-buffer) t))) | |
789 | (if insert | |
790 | (if (> (length keys1) 0) | |
791 | (princ (format "%s (%s)" keys1 definition)) | |
792 | (princ (format "M-x %s RET" definition))) | |
793 | (if (> (length keys1) 0) | |
794 | (princ (format "%s is on %s" definition keys1)) | |
795 | (princ (format "%s is not on any key" definition))))) | |
54c0b967 RS |
796 | nil) |
797 | ||
a130d829 | 798 | (defun locate-library (library &optional nosuffix path interactive-call) |
2747503c | 799 | "Show the precise file name of Emacs library LIBRARY. |
433ae6f6 RS |
800 | This command searches the directories in `load-path' like `M-x load-library' |
801 | to find the file that `M-x load-library RET LIBRARY RET' would load. | |
802 | Optional second arg NOSUFFIX non-nil means don't add suffixes `.elc' or `.el' | |
9dc176a0 RS |
803 | to the specified name LIBRARY. |
804 | ||
805 | If the optional third arg PATH is specified, that list of directories | |
806 | is used instead of `load-path'." | |
a130d829 RS |
807 | (interactive (list (read-string "Locate library: ") |
808 | nil nil | |
809 | t)) | |
dd557bb8 | 810 | (let (result) |
a130d829 RS |
811 | (catch 'answer |
812 | (mapcar | |
a1c9f209 EN |
813 | (lambda (dir) |
814 | (mapcar | |
815 | (lambda (suf) | |
816 | (let ((try (expand-file-name (concat library suf) dir))) | |
817 | (and (file-readable-p try) | |
818 | (null (file-directory-p try)) | |
819 | (progn | |
820 | (setq result try) | |
821 | (throw 'answer try))))) | |
822 | (if nosuffix | |
823 | '("") | |
dd557bb8 KH |
824 | '(".elc" ".el" "") |
825 | ;;; load doesn't handle this yet. | |
826 | ;;; (let ((basic '(".elc" ".el" "")) | |
827 | ;;; (compressed '(".Z" ".gz" ""))) | |
828 | ;;; ;; If autocompression mode is on, | |
829 | ;;; ;; consider all combinations of library suffixes | |
830 | ;;; ;; and compression suffixes. | |
831 | ;;; (if (rassq 'jka-compr-handler file-name-handler-alist) | |
832 | ;;; (apply 'nconc | |
833 | ;;; (mapcar (lambda (compelt) | |
834 | ;;; (mapcar (lambda (baselt) | |
835 | ;;; (concat baselt compelt)) | |
836 | ;;; basic)) | |
837 | ;;; compressed)) | |
838 | ;;; basic)) | |
839 | ))) | |
a130d829 RS |
840 | (or path load-path))) |
841 | (and interactive-call | |
842 | (if result | |
843 | (message "Library is file %s" result) | |
844 | (message "No library %s in search path" library))) | |
845 | result)) | |
1a06eabd | 846 | |
400a1b1f RS |
847 | \f |
848 | ;;; Grokking cross-reference information in doc strings and | |
849 | ;;; hyperlinking it. | |
850 | ||
851 | ;; This may have some scope for extension and the same or something | |
852 | ;; similar should be done for widget doc strings, which currently use | |
853 | ;; another mechanism. | |
854 | ||
855 | (defcustom help-highlight-p t | |
856 | "*If non-nil, `help-make-xrefs' highlight cross-references. | |
857 | Under a window system it highlights them with face defined by | |
858 | `help-highlight-face'. On a character terminal highlighted | |
859 | references look like cross-references in info mode." | |
860 | :group 'help | |
861 | :version "20.3" | |
862 | :type 'boolean) | |
863 | ||
864 | (defcustom help-highlight-face 'underline | |
865 | "Face used by `help-make-xrefs' to highlight cross-references. | |
866 | Must be previously-defined." | |
867 | :group 'help | |
868 | :version "20.3" | |
7f082394 | 869 | :type 'face) |
400a1b1f RS |
870 | |
871 | (defvar help-back-label "[back]" | |
872 | "Label to use by `help-make-xrefs' for the go-back reference.") | |
873 | ||
874 | (defvar help-xref-symbol-regexp | |
875 | (concat "\\(\\<\\(\\(variable\\|option\\)\\|" | |
876 | "\\(function\\|command\\)\\|" | |
877 | "\\(symbol\\)\\)\\s-+\\)?" | |
878 | ;; Note starting with word-syntax character: | |
879 | "`\\(\\sw\\(\\sw\\|\\s_\\)+\\)'") | |
880 | "Regexp matching doc string references to symbols. | |
881 | ||
882 | The words preceding the quoted symbol can be used in doc strings to | |
883 | distinguish references to variables, functions and symbols.") | |
884 | ||
885 | (defvar help-xref-info-regexp | |
886 | "\\<info\\s-+node\\s-`\\([^']+\\)'" | |
887 | "Regexp matching doc string references to an Info node.") | |
888 | ||
889 | (defun help-setup-xref (item interactive-p) | |
890 | "Invoked from commands using the \"*Help*\" buffer to install some xref info. | |
891 | ||
892 | ITEM is a (function . args) pair appropriate for recreating the help | |
893 | buffer after following a reference. INTERACTIVE-P is non-nil if the | |
894 | calling command was invoked interactively. In this case the stack of | |
895 | items for help buffer \"back\" buttons is cleared." | |
896 | (if interactive-p | |
897 | (setq help-xref-stack nil)) | |
898 | (setq help-xref-stack-item item)) | |
899 | ||
900 | (defun help-make-xrefs (&optional buffer) | |
901 | "Parse and hyperlink documentation cross-references in the given BUFFER. | |
902 | ||
903 | Find cross-reference information in a buffer and, if | |
904 | `help-highlight-p' is non-nil, highlight it with face defined by | |
905 | `help-highlight-face'; activate such cross references for selection | |
906 | with `help-follow'. Cross-references have the canonical form `...' | |
907 | and the type of reference may be disambiguated by the preceding | |
908 | word(s) used in `help-xref-symbol-regexp'. | |
909 | ||
910 | A special reference `back' is made to return back through a stack of | |
911 | help buffers. Variable `help-back-label' specifies the text for | |
912 | that." | |
913 | (interactive "b") | |
914 | (save-excursion | |
915 | (set-buffer (or buffer (current-buffer))) | |
916 | (goto-char (point-min)) | |
917 | ;; Skip the header-type info, though it might be useful to parse | |
918 | ;; it at some stage (e.g. "function in `library'"). | |
919 | (forward-paragraph) | |
920 | (let ((old-modified (buffer-modified-p))) | |
921 | (let ((stab (syntax-table)) | |
922 | (case-fold-search t) | |
923 | (inhibit-read-only t)) | |
924 | (set-syntax-table emacs-lisp-mode-syntax-table) | |
925 | ;; The following should probably be abstracted out. | |
926 | (unwind-protect | |
927 | (progn | |
928 | ;; Quoted symbols | |
929 | (save-excursion | |
930 | (while (re-search-forward help-xref-symbol-regexp nil t) | |
931 | (let* ((data (match-string 6)) | |
932 | (sym (intern-soft data))) | |
933 | (if sym | |
934 | (cond | |
935 | ((match-string 3) ; `variable' &c | |
936 | (and (boundp sym) ; `variable' doesn't ensure | |
937 | ; it's actually bound | |
938 | (help-xref-button 6 #'describe-variable sym))) | |
939 | ((match-string 4) ; `function' &c | |
940 | (and (fboundp sym) ; similarly | |
941 | (help-xref-button 6 #'describe-function sym))) | |
942 | ((match-string 5)) ; nothing for symbol | |
943 | ((and (boundp sym) (fboundp sym)) | |
944 | ;; We can't intuit whether to use the | |
945 | ;; variable or function doc -- supply both. | |
946 | (help-xref-button 6 #'help-xref-interned sym)) | |
947 | ((boundp sym) | |
948 | (help-xref-button 6 #'describe-variable sym)) | |
949 | ((fboundp sym) | |
950 | (help-xref-button 6 #'describe-function sym))))))) | |
951 | ;; Info references | |
952 | (save-excursion | |
953 | (while (re-search-forward help-xref-info-regexp nil t) | |
954 | (help-xref-button 1 #'Info-goto-node (list (match-data 1))))) | |
955 | ;; An obvious case of a key substitution: | |
956 | (save-excursion | |
957 | (while (re-search-forward | |
958 | "\\<M-x\\s-+\\(\\sw\\(\\sw\\|\\s_\\)+\\)" nil t) | |
959 | (let ((sym (intern-soft (match-string 1)))) | |
960 | (if (fboundp sym) | |
ff3453e4 DL |
961 | (help-xref-button 1 #'describe-function sym))))) |
962 | ;; Look for commands in whole keymap substitutions: | |
963 | (save-excursion | |
964 | ;; Find a header and the column at which the command | |
965 | ;; name will be found. | |
966 | (while (re-search-forward "^key +binding\n\\(-+ +\\)-+\n\n" | |
967 | nil t) | |
968 | (let ((col (- (match-end 1) (match-beginning 1)))) | |
969 | (while | |
970 | ;; Ignore single blank lines in table, but not | |
971 | ;; double ones, which should terminate it. | |
972 | (and (looking-at "^\n?[^\n]") | |
973 | (progn | |
974 | (if (and (> (move-to-column col) 0) | |
975 | (looking-at "\\(\\sw\\|\\s_\\)+$")) | |
976 | ;; | |
977 | (let ((sym (intern-soft (match-string 0)))) | |
978 | (if (fboundp sym) | |
979 | (help-xref-button | |
980 | 0 #'describe-function sym)))) | |
981 | t) | |
982 | (zerop (forward-line)) | |
983 | (move-to-column 0))))))) | |
400a1b1f RS |
984 | (set-syntax-table stab)) |
985 | ;; Make a back-reference in this buffer if appropriate. | |
986 | (when help-xref-stack | |
987 | (goto-char (point-max)) | |
988 | (save-excursion | |
989 | (insert "\n\n" help-back-label)) | |
990 | ;; Just to provide the match data: | |
991 | (looking-at (concat "\n\n\\(" (regexp-quote help-back-label) "\\)")) | |
613a39b9 | 992 | (help-xref-button 1 #'help-xref-go-back (current-buffer)))) |
400a1b1f RS |
993 | ;; View mode steals RET from us. |
994 | (set (make-local-variable 'minor-mode-overriding-map-alist) | |
995 | (list (cons 'view-mode | |
996 | (let ((map (make-sparse-keymap))) | |
ff3453e4 | 997 | (set-keymap-parent map view-mode-map) |
400a1b1f RS |
998 | (define-key map "\r" 'help-follow) |
999 | map)))) | |
1000 | (set-buffer-modified-p old-modified)))) | |
1001 | ||
1002 | (defun help-xref-button (match-number function data) | |
1003 | "Make a hyperlink for cross-reference text previously matched. | |
1004 | ||
1005 | MATCH-NUMBER is the subexpression of interest in the last matched | |
1006 | regexp. FUNCTION is a function to invoke when the button is | |
1007 | activated, applied to DATA. DATA may be a single value or a list. | |
1008 | See `help-make-xrefs'." | |
ff3453e4 | 1009 | (add-text-properties (match-beginning match-number) |
400a1b1f | 1010 | (match-end match-number) |
ff3453e4 | 1011 | (list 'mouse-face 'highlight |
400a1b1f RS |
1012 | 'help-xref (cons function |
1013 | (if (listp data) | |
1014 | data | |
1015 | (list data))))) | |
ff3453e4 DL |
1016 | (if help-highlight-p |
1017 | (put-text-property (match-beginning match-number) | |
1018 | (match-end match-number) | |
1019 | 'face help-highlight-face))) | |
400a1b1f RS |
1020 | |
1021 | \f | |
1022 | ;; Additional functions for (re-)creating types of help buffers. | |
1023 | (defun help-xref-interned (symbol) | |
1024 | "Follow a hyperlink which appeared to be an arbitrary interned SYMBOL. | |
1025 | ||
1026 | Both variable and function documentation are extracted into a single | |
1027 | help buffer." | |
1028 | (let ((fdoc (describe-function symbol))) | |
1029 | (describe-variable symbol) | |
1030 | ;; We now have a help buffer on the variable. Insert the function | |
1031 | ;; text after it. | |
1032 | (goto-char (point-max)) | |
1033 | (insert "\n\n" fdoc)) | |
1034 | (goto-char (point-min)) | |
1035 | (help-setup-xref (cons #'help-xref-interned symbol) nil)) | |
1036 | ||
1037 | (defun help-xref-mode (buffer) | |
1038 | "Do a `describe-mode' for the specified BUFFER." | |
1039 | (save-excursion | |
1040 | (set-buffer buffer) | |
1041 | (describe-mode))) | |
1042 | \f | |
1043 | ;;; Navigation/hyperlinking with xrefs | |
1044 | ||
1045 | (defun help-follow-mouse (click) | |
1046 | "Follow the cross-reference that you click on." | |
1047 | (interactive "e") | |
1048 | (save-excursion | |
1049 | (let* ((start (event-start click)) | |
1050 | (window (car start)) | |
1051 | (pos (car (cdr start)))) | |
1052 | (set-buffer (window-buffer window)) | |
1053 | (help-follow pos)))) | |
1054 | ||
613a39b9 RS |
1055 | (defun help-xref-go-back (buffer) |
1056 | "Go back to the previous help buffer text using info on `help-xref-stack'." | |
400a1b1f | 1057 | (interactive) |
613a39b9 RS |
1058 | (let (item method args) |
1059 | (with-current-buffer buffer | |
1060 | (when help-xref-stack | |
1061 | (setq help-xref-stack (cdr help-xref-stack)) ; due to help-follow | |
1062 | (setq item (car help-xref-stack) | |
1063 | method (car item) | |
1064 | args (cdr item)) | |
1065 | (setq help-xref-stack (cdr help-xref-stack)))) | |
1066 | (if (listp args) | |
1067 | (apply method args) | |
1068 | (funcall method args)))) | |
400a1b1f RS |
1069 | |
1070 | (defun help-go-back () | |
1071 | (interactive) | |
1072 | (help-follow (1- (point-max)))) | |
1073 | ||
1074 | (defun help-follow (&optional pos) | |
1075 | "Follow cross-reference at POS, defaulting to point. | |
1076 | ||
1077 | For the cross-reference format, see `help-make-xrefs'." | |
1078 | (interactive "d") | |
1079 | (let* ((help-data (get-text-property pos 'help-xref)) | |
1080 | (method (car help-data)) | |
1081 | (args (cdr help-data))) | |
1082 | (setq help-xref-stack (cons help-xref-stack-item help-xref-stack)) | |
1083 | (setq help-xref-stack-item nil) | |
1084 | (when help-data | |
1085 | ;; There is a reference at point. Follow it. | |
1086 | (apply method args)))) | |
1087 | ||
1088 | ;; For tabbing through buffer. | |
1089 | (defun help-next-ref () | |
1090 | "Find the next help cross-reference in the buffer." | |
1091 | (interactive) | |
1092 | (let (pos) | |
1093 | (while (not pos) | |
1094 | (if (get-text-property (point) 'help-xref) ; move off reference | |
ff3453e4 DL |
1095 | (goto-char (or (next-single-property-change (point) 'help-xref) |
1096 | (point)))) | |
400a1b1f RS |
1097 | (cond ((setq pos (next-single-property-change (point) 'help-xref)) |
1098 | (if pos (goto-char pos))) | |
1099 | ((bobp) | |
1100 | (message "No cross references in the buffer.") | |
1101 | (setq pos t)) | |
1102 | (t ; be circular | |
1103 | (goto-char (point-min))))))) | |
1104 | ||
1105 | (defun help-previous-ref () | |
1106 | "Find the previous help cross-reference in the buffer." | |
1107 | (interactive) | |
1108 | (let (pos) | |
1109 | (while (not pos) | |
1110 | (if (get-text-property (point) 'help-xref) ; move off reference | |
1111 | (goto-char (or (previous-single-property-change (point) 'help-xref) | |
1112 | (point)))) | |
1113 | (cond ((setq pos (previous-single-property-change (point) 'help-xref)) | |
1114 | (if pos (goto-char pos))) | |
1115 | ((bobp) | |
1116 | (message "No cross references in the buffer.") | |
1117 | (setq pos t)) | |
1118 | (t ; be circular | |
1119 | (goto-char (point-max))))))) | |
1120 | ||
1a06eabd | 1121 | ;;; help.el ends here |