x_display_unibyte_char_with_fontset renamed to
[bpt/emacs.git] / lisp / info.el
CommitLineData
e3431643 1;;; info.el --- info package for Emacs.
e5167999 2
96ee3f29 3;; Copyright (C) 1985, 86, 92, 93, 94, 95, 96, 97, 98 Free Software
3ec18f3f 4;; Foundation, Inc.
3a801d0c 5
e5167999 6;; Maintainer: FSF
fd7fa35a 7;; Keywords: help
e5167999 8
a384cab3
JB
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
e5167999 13;; the Free Software Foundation; either version 2, or (at your option)
a384cab3
JB
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
b578f267
EN
22;; along with GNU Emacs; see the file COPYING. If not, write to the
23;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24;; Boston, MA 02111-1307, USA.
a384cab3 25
e5167999
ER
26;;; Commentary:
27
b578f267 28;; Note that nowadays we expect info files to be made using makeinfo.
e5167999
ER
29
30;;; Code:
31
ded3e3d8
RS
32(defgroup info nil
33 "Info subsystem"
34 :group 'help
35 :group 'docs)
36
37
a384cab3
JB
38(defvar Info-history nil
39 "List of info nodes user has visited.
40Each element of list is a list (FILENAME NODENAME BUFFERPOS).")
41
ded3e3d8 42(defcustom Info-enable-edit nil
d5913bf6 43 "*Non-nil means the \\<Info-mode-map>\\[Info-edit] command in Info can edit the current node.
ada0a60d
RS
44This is convenient if you want to write info files by hand.
45However, we recommend that you not do this.
46It is better to write a Texinfo file and generate the Info file from that,
ded3e3d8
RS
47because that gives you a printed manual as well."
48 :type 'boolean
49 :group 'info)
a384cab3 50
67f445d7 51(defvar Info-enable-active-nodes nil
a384cab3
JB
52 "Non-nil allows Info to execute Lisp code associated with nodes.
53The Lisp code is executed when the node is selected.")
67f445d7 54(put 'Info-enable-active-nodes 'risky-local-variable t)
a384cab3 55
ded3e3d8
RS
56(defcustom Info-fontify t
57 "*Non-nil enables highlighting and fonts in Info nodes."
58 :type 'boolean
59 :group 'info)
552775bd 60
93480d70
RS
61(defface info-node
62 '((t (:bold t :italic t)))
63 "Face for Info node names."
64 :group 'info)
65
66(defface info-menu-5
67 '((t (:underline t)))
68 "Face for the fifth and tenth `*' in an Info menu."
69 :group 'info)
70
71(defface info-xref
72 '((t (:bold t)))
73 "Face for Info cross-references."
74 :group 'info)
75
ded3e3d8
RS
76(defcustom Info-fontify-maximum-menu-size 30000
77 "*Maximum size of menu to fontify if `Info-fontify' is non-nil."
78 :type 'integer
79 :group 'info)
bdf62a4d 80
118199d5 81(defvar Info-directory-list
47d53769 82 (let ((path (getenv "INFOPATH"))
01a2b480
RS
83 ;; This is for older Emacs versions
84 ;; which might get this info.el from the Texinfo distribution.
85 (path-separator (if (boundp 'path-separator) path-separator
86 (if (eq system-type 'ms-dos) ";" ":")))
0bf65919 87 (source (expand-file-name "info/" source-directory))
0e7f9b35 88 (sibling (if installation-directory
0bf65919
RS
89 (expand-file-name "info/" installation-directory)))
90 alternative)
118199d5
RS
91 (if path
92 (let ((list nil)
93 idx)
94 (while (> (length path) 0)
792e773a 95 (setq idx (or (string-match path-separator path) (length path))
118199d5
RS
96 list (cons (substring path 0 idx) list)
97 path (substring path (min (1+ idx)
98 (length path)))))
99 (nreverse list))
0bf65919
RS
100 (if (and sibling (file-exists-p sibling))
101 (setq alternative sibling)
102 (setq alternative source))
103 (if (or (member alternative Info-default-directory-list)
104 (not (file-exists-p alternative))
57f2b3e4 105 ;; On DOS/NT, we use movable executables always,
718b4478 106 ;; and we must always find the Info dir at run time.
57f2b3e4 107 (if (or (eq system-type 'ms-dos) (eq system-type 'windows-nt))
718b4478
RS
108 nil
109 ;; Use invocation-directory for Info only if we used it for
110 ;; exec-directory also.
111 (not (string= exec-directory
0e7f9b35
RS
112 (expand-file-name "lib-src/"
113 installation-directory)))))
47d53769 114 Info-default-directory-list
0bf65919
RS
115 (reverse (cons alternative
116 (cdr (reverse Info-default-directory-list)))))))
a384cab3 117 "List of directories to search for Info documentation files.
aea2a8da 118nil means not yet initialized. In this case, Info uses the environment
44c327f9 119variable INFOPATH to initialize it, or `Info-default-directory-list'
e5bd2125
RS
120if there is no INFOPATH variable in the environment.
121The last element of `Info-default-directory-list' is the directory
5c11086b 122where Emacs installs the Info files that come with it.
5da0f945
RS
123
124If you run the Emacs executable from the `src' directory in the Emacs
125source tree, the `info' directory in the source tree is used as the last
126element, in place of the installation Info directory. This is useful
127when you run a version of Emacs without installing it.")
a384cab3 128
ded3e3d8 129(defcustom Info-additional-directory-list nil
bdf62a4d 130 "List of additional directories to search for Info documentation files.
ded3e3d8
RS
131These directories are not searched for merging the `dir' file."
132 :type '(repeat directory)
133 :group 'info)
bdf62a4d 134
a384cab3 135(defvar Info-current-file nil
d6be34f3
RS
136 "Info file that Info is now looking at, or nil.
137This is the name that was specified in Info, not the actual file name.
138It doesn't contain directory names or file name extensions added by Info.")
a384cab3
JB
139
140(defvar Info-current-subfile nil
141 "Info subfile that is actually in the *info* buffer now,
142or nil if current info file is not split into subfiles.")
143
144(defvar Info-current-node nil
145 "Name of node that Info is now looking at, or nil.")
146
c5fe2ff1 147(defvar Info-tag-table-marker nil
a384cab3
JB
148 "Marker pointing at beginning of current Info file's tag table.
149Marker points nowhere if file has no tag table.")
150
c5fe2ff1
RS
151(defvar Info-tag-table-buffer nil
152 "Buffer used for indirect tag tables.")
153
552775bd
RS
154(defvar Info-current-file-completions nil
155 "Cached completion list for current Info file.")
156
1143a6b0
ER
157(defvar Info-index-alternatives nil
158 "List of possible matches for last Info-index command.")
159
552775bd
RS
160(defvar Info-standalone nil
161 "Non-nil if Emacs was started solely as an Info browser.")
162
bd0c9168 163(defvar Info-suffix-list
3c19e6c1
RS
164 ;; The MS-DOS list should work both when long file names are
165 ;; supported (Windows 9X), and when only 8+3 file names are available.
bd0c9168
RS
166 (if (eq system-type 'ms-dos)
167 '( (".gz" . "gunzip")
168 (".z" . "gunzip")
3c19e6c1
RS
169 (".inz" . "gunzip")
170 (".igz" . "gunzip")
171 (".info.Z" . "gunzip")
172 (".info.gz" . "gunzip")
173 ("-info.Z" . "gunzip")
174 ("-info.gz" . "gunzip")
175 ("/index.gz". "gunzip")
176 ("/index.z" . "gunzip")
9b842cab 177 (".inf" . nil)
3c19e6c1
RS
178 (".info" . nil)
179 ("-info" . nil)
180 ("/index" . nil)
bd0c9168 181 ("" . nil))
a70dc410
RS
182 '( (".info.Z". "uncompress")
183 (".info.Y". "unyabba")
184 (".info.gz". "gunzip")
185 (".info.z". "gunzip")
186 (".info". nil)
187 ("-info.Z". "uncompress")
188 ("-info.Y". "unyabba")
189 ("-info.gz". "gunzip")
190 ("-info.z". "gunzip")
191 ("-info". nil)
192 ("/index.Z". "uncompress")
193 ("/index.Y". "unyabba")
194 ("/index.gz". "gunzip")
195 ("/index.z". "gunzip")
196 ("/index". nil)
197 (".Z". "uncompress")
198 (".Y". "unyabba")
199 (".gz". "gunzip")
200 (".z". "gunzip")
201 ("". nil)))
1143a6b0
ER
202 "List of file name suffixes and associated decoding commands.
203Each entry should be (SUFFIX . STRING); the file is given to
97f99202
KH
204the command as standard input. If STRING is nil, no decoding is done.
205Because the SUFFIXes are tried in order, the empty string should
206be last in the list.")
1143a6b0 207
c8e9dd54 208;; Concatenate SUFFIX onto FILENAME. SUFFIX should start with a dot.
bd0c9168
RS
209;; First, on ms-dos, delete some of the extension in FILENAME
210;; to make room.
211(defun info-insert-file-contents-1 (filename suffix)
212 (if (not (eq system-type 'ms-dos))
213 (concat filename suffix)
214 (let* ((sans-exts (file-name-sans-extension filename))
c8e9dd54
RS
215 ;; How long is the extension in FILENAME (not counting the dot).
216 (ext-len (max 0 (- (length filename) (length sans-exts) 1)))
217 ext-left)
9b842cab 218 ;; SUFFIX starts with a dot. If FILENAME already has one,
1583fe94 219 ;; get rid of the one in SUFFIX (unless suffix is empty).
c8e9dd54 220 (or (and (<= ext-len 0)
9b842cab 221 (not (eq (aref filename (1- (length filename))) ?.)))
1583fe94 222 (= (length suffix) 0)
9b842cab 223 (setq suffix (substring suffix 1)))
c8e9dd54
RS
224 ;; How many chars of that extension should we keep?
225 (setq ext-left (min ext-len (max 0 (- 3 (length suffix)))))
bd0c9168
RS
226 ;; Get rid of the rest of the extension, and add SUFFIX.
227 (concat (substring filename 0 (- (length filename)
228 (- ext-len ext-left)))
229 suffix))))
230
9c7924d5
RS
231(defun info-file-exists-p (filename)
232 (and (file-exists-p filename)
233 (not (file-directory-p filename))))
234
1143a6b0
ER
235(defun info-insert-file-contents (filename &optional visit)
236 "Insert the contents of an info file in the current buffer.
237Do the right thing if the file has been compressed or zipped."
97f99202
KH
238 (let ((tail Info-suffix-list)
239 fullname decoder)
240 (if (file-exists-p filename)
bd0c9168
RS
241 ;; FILENAME exists--see if that name contains a suffix.
242 ;; If so, set DECODE accordingly.
97f99202
KH
243 (progn
244 (while (and tail
245 (not (string-match
246 (concat (regexp-quote (car (car tail))) "$")
247 filename)))
248 (setq tail (cdr tail)))
249 (setq fullname filename
250 decoder (cdr (car tail))))
bd0c9168 251 ;; Try adding suffixes to FILENAME and see if we can find something.
97f99202 252 (while (and tail
9c7924d5
RS
253 (not (info-file-exists-p (info-insert-file-contents-1
254 filename (car (car tail))))))
97f99202 255 (setq tail (cdr tail)))
bd0c9168
RS
256 ;; If we found a file with a suffix, set DECODER according to the suffix
257 ;; and set FULLNAME to the file's actual name.
9b842cab 258 (setq fullname (info-insert-file-contents-1 filename (car (car tail)))
97f99202
KH
259 decoder (cdr (car tail)))
260 (or tail
bd0c9168 261 (error "Can't find %s or any compressed version of it" filename)))
e175bda2
RS
262 ;; check for conflict with jka-compr
263 (if (and (featurep 'jka-compr)
264 (jka-compr-installed-p)
265 (jka-compr-get-compression-info fullname))
266 (setq decoder nil))
97f99202 267 (if decoder
3c19e6c1
RS
268 (progn
269 (insert-file-contents-literally fullname visit)
270 (let ((buffer-read-only nil)
271 (coding-system-for-write 'no-conversion)
272 (default-directory (or (file-name-directory fullname)
273 default-directory)))
274 (call-process-region (point-min) (point-max) decoder t t)))
275 (insert-file-contents fullname visit))))
1143a6b0 276
a6e4564e
RS
277;;;###autoload
278(defun info-other-window (&optional file)
279 "Like `info' but show the Info buffer in another window."
280 (interactive (if current-prefix-arg
281 (list (read-file-name "Info file name: " nil nil t))))
282 (let (same-window-buffer-names)
283 (info file)))
284
d282974b 285;;;###autoload (add-hook 'same-window-buffer-names "*info*")
211d6309 286
a384cab3
JB
287;;;###autoload
288(defun info (&optional file)
289 "Enter Info, the documentation browser.
290Optional argument FILE specifies the file to examine;
291the default is the top-level directory of Info.
292
293In interactive use, a prefix argument directs this command
e341dab2
RS
294to read a file name from the minibuffer.
295
296The search path for Info files is in the variable `Info-directory-list'.
297The top-level Info directory is made by combining all the files named `dir'
298in all the directories in that path."
a384cab3
JB
299 (interactive (if current-prefix-arg
300 (list (read-file-name "Info file name: " nil nil t))))
a384cab3 301 (if file
35d2d241
RS
302 (progn (pop-to-buffer "*info*")
303 (Info-goto-node (concat "(" file ")")))
304 (if (get-buffer "*info*")
305 (pop-to-buffer "*info*")
306 (Info-directory))))
a384cab3 307
552775bd
RS
308;;;###autoload
309(defun info-standalone ()
310 "Run Emacs as a standalone Info reader.
311Usage: emacs -f info-standalone [filename]
312In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
313 (setq Info-standalone t)
314 (if (and command-line-args-left
315 (not (string-match "^-" (car command-line-args-left))))
316 (condition-case err
317 (progn
318 (info (car command-line-args-left))
319 (setq command-line-args-left (cdr command-line-args-left)))
320 (error (send-string-to-terminal
321 (format "%s\n" (if (eq (car-safe err) 'error)
322 (nth 1 err) err)))
323 (save-buffers-kill-emacs)))
324 (info)))
325
a384cab3
JB
326;; Go to an info node specified as separate filename and nodename.
327;; no-going-back is non-nil if recovering from an error in this function;
328;; it says do not attempt further (recursive) error recovery.
329(defun Info-find-node (filename nodename &optional no-going-back)
330 ;; Convert filename to lower case if not found as specified.
331 ;; Expand it.
332 (if filename
333 (let (temp temp-downcase found)
37a3ff5b
RS
334 (setq filename (substitute-in-file-name filename))
335 (if (string= (downcase filename) "dir")
336 (setq found t)
337 (let ((dirs (if (string-match "^\\./" filename)
338 ;; If specified name starts with `./'
339 ;; then just try current directory.
340 '("./")
341 (if (file-name-absolute-p filename)
342 ;; No point in searching for an
343 ;; absolute file name
344 '(nil)
345 (if Info-additional-directory-list
346 (append Info-directory-list
347 Info-additional-directory-list)
348 Info-directory-list)))))
349 ;; Search the directory list for file FILENAME.
350 (while (and dirs (not found))
351 (setq temp (expand-file-name filename (car dirs)))
352 (setq temp-downcase
353 (expand-file-name (downcase filename) (car dirs)))
354 ;; Try several variants of specified name.
355 (let ((suffix-list Info-suffix-list))
356 (while (and suffix-list (not found))
357 (cond ((info-file-exists-p
358 (info-insert-file-contents-1
359 temp (car (car suffix-list))))
360 (setq found temp))
361 ((info-file-exists-p
362 (info-insert-file-contents-1
363 temp-downcase (car (car suffix-list))))
364 (setq found temp-downcase)))
365 (setq suffix-list (cdr suffix-list))))
366 (setq dirs (cdr dirs)))))
367 (if found
368 (setq filename found)
369 (error "Info file %s does not exist" filename))))
370 ;; Record the node we are leaving.
371 (if (and Info-current-file (not no-going-back))
372 (setq Info-history
373 (cons (list Info-current-file Info-current-node (point))
374 Info-history)))
a384cab3 375 ;; Go into info buffer.
c5fe2ff1 376 (or (eq major-mode 'Info-mode) (pop-to-buffer "*info*"))
9e5c2f50 377 (buffer-disable-undo (current-buffer))
a384cab3
JB
378 (or (eq major-mode 'Info-mode)
379 (Info-mode))
380 (widen)
381 (setq Info-current-node nil)
382 (unwind-protect
1bcedb3b
RS
383 ;; Bind case-fold-search in case the user sets it to nil.
384 (let ((case-fold-search t))
37a3ff5b
RS
385 ;; Switch files if necessary
386 (or (null filename)
387 (equal Info-current-file filename)
388 (let ((buffer-read-only nil))
389 (setq Info-current-file nil
390 Info-current-subfile nil
391 Info-current-file-completions nil
392 buffer-file-name nil)
393 (erase-buffer)
394 (if (eq filename t)
395 (Info-insert-dir)
396 (info-insert-file-contents filename t)
397 (setq default-directory (file-name-directory filename)))
398 (set-buffer-modified-p nil)
399 ;; See whether file has a tag table. Record the location if yes.
400 (goto-char (point-max))
401 (forward-line -8)
402 ;; Use string-equal, not equal, to ignore text props.
403 (if (not (or (string-equal nodename "*")
404 (not
405 (search-forward "\^_\nEnd tag table\n" nil t))))
406 (let (pos)
407 ;; We have a tag table. Find its beginning.
408 ;; Is this an indirect file?
409 (search-backward "\nTag table:\n")
410 (setq pos (point))
411 (if (save-excursion
412 (forward-line 2)
413 (looking-at "(Indirect)\n"))
414 ;; It is indirect. Copy it to another buffer
415 ;; and record that the tag table is in that buffer.
416 (let ((buf (current-buffer))
417 (tagbuf
418 (or Info-tag-table-buffer
419 (generate-new-buffer " *info tag table*"))))
420 (setq Info-tag-table-buffer tagbuf)
421 (save-excursion
422 (set-buffer tagbuf)
9e5c2f50 423 (buffer-disable-undo (current-buffer))
37a3ff5b
RS
424 (setq case-fold-search t)
425 (erase-buffer)
426 (insert-buffer-substring buf))
427 (set-marker Info-tag-table-marker
428 (match-end 0) tagbuf))
429 (set-marker Info-tag-table-marker pos)))
430 (set-marker Info-tag-table-marker nil))
431 (setq Info-current-file
432 (if (eq filename t) "dir" filename))))
433 ;; Use string-equal, not equal, to ignore text props.
434 (if (string-equal nodename "*")
435 (progn (setq Info-current-node nodename)
436 (Info-set-mode-line))
437 ;; Possibilities:
438 ;;
439 ;; 1. Anchor found in tag table
440 ;; 2. Anchor *not* in tag table
441 ;;
442 ;; 3. Node found in tag table
443 ;; 4. Node *not* found in tag table, but found in file
444 ;; 5. Node *not* in tag table, and *not* in file
445 ;;
446 ;; *Or* the same, but in an indirect subfile.
447
448 ;; Search file for a suitable node.
449 (let ((guesspos (point-min))
450 (regexp
451 (concat "\\(Node:\\|Ref:\\) *"
452 (regexp-quote nodename)
453 " *[,\t\n\177]")))
454
455 ;; First, search a tag table, if any
456 (if (marker-position Info-tag-table-marker)
457
458 (let (found-in-tag-table
459 found-mode
460 (m Info-tag-table-marker))
461 (save-excursion
462 (set-buffer (marker-buffer m))
463 (goto-char m)
464 (beginning-of-line) ; so re-search will work.
465
466 ;; Search tag table
467 (setq found-in-tag-table
468 (re-search-forward regexp nil t))
469 (if found-in-tag-table
470 (setq guesspos (read (current-buffer))))
471 (setq found-mode major-mode))
472
473 ;; Indirect file among split files
474 (if found-in-tag-table
475 (progn
476 ;; If this is an indirect file, determine
477 ;; which file really holds this node and
478 ;; read it in.
479 (if (not (eq found-mode 'Info-mode))
480 ;; Note that the current buffer must be
481 ;; the *info* buffer on entry to
482 ;; Info-read-subfile. Thus the hackery
483 ;; above.
484 (setq guesspos (Info-read-subfile guesspos)))))
485
486 ;; Handle anchor
487 (if (and found-in-tag-table
488 (string-equal "Ref:" (match-string 1)))
489 (goto-char guesspos)
490
491 ;; Else we may have a node, which we search for:
fabcd845
RS
492 (goto-char (max (point-min)
493 (- (byte-to-position guesspos) 1000)))
37a3ff5b
RS
494 ;; Now search from our advised position
495 ;; (or from beg of buffer)
496 ;; to find the actual node.
497 (catch 'foo
498 (while (search-forward "\n\^_" nil t)
499 (forward-line 1)
500 (let ((beg (point)))
501 (forward-line 1)
502 (if (re-search-backward regexp beg t)
503 (progn
504 (beginning-of-line)
505 (throw 'foo t)))))
506 (error
507 "No such anchor in tag table or node in tag table or file: %s"
508 nodename))))))
509
9d499629
RS
510 (Info-select-node)
511 (goto-char (point-min))))
a384cab3
JB
512 ;; If we did not finish finding the specified node,
513 ;; go back to the previous one.
4db579ab 514 (or Info-current-node no-going-back (null Info-history)
37a3ff5b
RS
515 (let ((hist (car Info-history)))
516 (setq Info-history (cdr Info-history))
517 (Info-find-node (nth 0 hist) (nth 1 hist) t)
518 (goto-char (nth 2 hist))))))
a384cab3 519
44c327f9
JB
520;; Cache the contents of the (virtual) dir file, once we have merged
521;; it for the first time, so we can save time subsequently.
7ea13762
RS
522(defvar Info-dir-contents nil)
523
44c327f9
JB
524;; Cache for the directory we decided to use for the default-directory
525;; of the merged dir text.
526(defvar Info-dir-contents-directory nil)
527
c142ab2d
RS
528;; Record the file attributes of all the files from which we
529;; constructed Info-dir-contents.
530(defvar Info-dir-file-attributes nil)
531
7ea13762 532;; Construct the Info directory node by merging the files named `dir'
44c327f9
JB
533;; from various directories. Set the *info* buffer's
534;; default-directory to the first directory we actually get any text
535;; from.
7ea13762 536(defun Info-insert-dir ()
c142ab2d
RS
537 (if (and Info-dir-contents Info-dir-file-attributes
538 ;; Verify that none of the files we used has changed
539 ;; since we used it.
540 (eval (cons 'and
541 (mapcar '(lambda (elt)
825d6f08
RS
542 (let ((curr (file-attributes (car elt))))
543 ;; Don't compare the access time.
544 (if curr (setcar (nthcdr 4 curr) 0))
545 (setcar (nthcdr 4 (cdr elt)) 0)
546 (equal (cdr elt) curr)))
c142ab2d 547 Info-dir-file-attributes))))
7ea13762
RS
548 (insert Info-dir-contents)
549 (let ((dirs Info-directory-list)
1bcedb3b
RS
550 ;; Bind this in case the user sets it to nil.
551 (case-fold-search t)
f4008b6e 552 buffers buffer others nodes dirs-done)
44c327f9 553
825d6f08
RS
554 (setq Info-dir-file-attributes nil)
555
44c327f9 556 ;; Search the directory list for the directory file.
7ea13762 557 (while dirs
8d1abb42
RS
558 (let ((truename (file-truename (expand-file-name (car dirs)))))
559 (or (member truename dirs-done)
560 (member (directory-file-name truename) dirs-done)
561 ;; Try several variants of specified name.
562 ;; Try upcasing, appending `.info', or both.
825d6f08
RS
563 (let* (file
564 (attrs
565 (or
566 (progn (setq file (expand-file-name "dir" truename))
567 (file-attributes file))
568 (progn (setq file (expand-file-name "DIR" truename))
569 (file-attributes file))
570 (progn (setq file (expand-file-name "dir.info" truename))
571 (file-attributes file))
572 (progn (setq file (expand-file-name "DIR.INFO" truename))
573 (file-attributes file)))))
8d1abb42
RS
574 (setq dirs-done
575 (cons truename
576 (cons (directory-file-name truename)
577 dirs-done)))
825d6f08
RS
578 (if attrs
579 (save-excursion
580 (or buffers
581 (message "Composing main Info directory..."))
2d41cf59 582 (set-buffer (generate-new-buffer " info dir"))
9d499629
RS
583 (condition-case nil
584 (progn
585 (insert-file-contents file)
586 (setq buffers (cons (current-buffer) buffers)
587 Info-dir-file-attributes
588 (cons (cons file attrs)
589 Info-dir-file-attributes)))
590 (error (kill-buffer (current-buffer))))))))
3296453b
KH
591 (or (cdr dirs) (setq Info-dir-contents-directory
592 (file-name-as-directory (car dirs))))
825d6f08
RS
593 (setq dirs (cdr dirs))))
594
4db579ab 595 (or buffers
81e14cb2 596 (error "Can't find the Info directory node"))
44c327f9 597 ;; Distinguish the dir file that comes with Emacs from all the
f4008b6e
RS
598 ;; others. Yes, that is really what this is supposed to do.
599 ;; If it doesn't work, fix it.
7ea13762
RS
600 (setq buffer (car buffers)
601 others (cdr buffers))
44c327f9 602
debcea0c
KH
603 ;; Insert the entire original dir file as a start; note that we've
604 ;; already saved its default directory to use as the default
605 ;; directory for the whole concatenation.
7ea13762 606 (insert-buffer buffer)
44c327f9 607
7ea13762
RS
608 ;; Look at each of the other buffers one by one.
609 (while others
610 (let ((other (car others)))
611 ;; In each, find all the menus.
612 (save-excursion
613 (set-buffer other)
614 (goto-char (point-min))
615 ;; Find each menu, and add an elt to NODES for it.
616 (while (re-search-forward "^\\* Menu:" nil t)
617 (let (beg nodename end)
618 (forward-line 1)
619 (setq beg (point))
3a6ade8a 620 (search-backward "\n\^_")
7ea13762
RS
621 (search-forward "Node: ")
622 (setq nodename (Info-following-node-name))
3a6ade8a 623 (search-forward "\n\^_" nil 'move)
7ea13762
RS
624 (beginning-of-line)
625 (setq end (point))
626 (setq nodes (cons (list nodename other beg end) nodes))))))
627 (setq others (cdr others)))
628 ;; Add to the main menu a menu item for each other node.
629 (re-search-forward "^\\* Menu:")
630 (forward-line 1)
631 (let ((menu-items '("top"))
632 (nodes nodes)
633 (case-fold-search t)
3a6ade8a 634 (end (save-excursion (search-forward "\^_" nil t) (point))))
7ea13762
RS
635 (while nodes
636 (let ((nodename (car (car nodes))))
c3a29d70
RS
637 (save-excursion
638 (or (member (downcase nodename) menu-items)
b37daea4 639 (re-search-forward (concat "^\\* +"
c3a29d70
RS
640 (regexp-quote nodename)
641 "::")
642 end t)
643 (progn
644 (insert "* " nodename "::" "\n")
645 (setq menu-items (cons nodename menu-items))))))
7ea13762
RS
646 (setq nodes (cdr nodes))))
647 ;; Now take each node of each of the other buffers
648 ;; and merge it into the main buffer.
649 (while nodes
650 (let ((nodename (car (car nodes))))
651 (goto-char (point-min))
652 ;; Find the like-named node in the main buffer.
3a6ade8a 653 (if (re-search-forward (concat "\n\^_.*\n.*Node: "
7ea13762
RS
654 (regexp-quote nodename)
655 "[,\n\t]")
656 nil t)
657 (progn
3a6ade8a 658 (search-forward "\n\^_" nil 'move)
25869acf
RS
659 (beginning-of-line)
660 (insert "\n"))
7ea13762
RS
661 ;; If none exists, add one.
662 (goto-char (point-max))
dfbf6104 663 (insert "\^_\nFile: dir\tNode: " nodename "\n\n* Menu:\n\n"))
7ea13762
RS
664 ;; Merge the text from the other buffer's menu
665 ;; into the menu in the like-named node in the main buffer.
25869acf 666 (apply 'insert-buffer-substring (cdr (car nodes))))
7ea13762
RS
667 (setq nodes (cdr nodes)))
668 ;; Kill all the buffers we just made.
669 (while buffers
670 (kill-buffer (car buffers))
825d6f08
RS
671 (setq buffers (cdr buffers)))
672 (message "Composing main Info directory...done"))
44c327f9
JB
673 (setq Info-dir-contents (buffer-string)))
674 (setq default-directory Info-dir-contents-directory))
7ea13762 675
c5fe2ff1
RS
676;; Note that on entry to this function the current-buffer must be the
677;; *info* buffer; not the info tags buffer.
a384cab3 678(defun Info-read-subfile (nodepos)
a361deba
RS
679 ;; NODEPOS is either a position (in the Info file as a whole,
680 ;; not relative to a subfile) or the name of a subfile.
a384cab3
JB
681 (let (lastfilepos
682 lastfilename)
a361deba
RS
683 (if (numberp nodepos)
684 (save-excursion
685 (set-buffer (marker-buffer Info-tag-table-marker))
686 (goto-char (point-min))
687 (search-forward "\n\^_")
688 (forward-line 2)
689 (catch 'foo
690 (while (not (looking-at "\^_"))
691 (if (not (eolp))
692 (let ((beg (point))
693 thisfilepos thisfilename)
694 (search-forward ": ")
695 (setq thisfilename (buffer-substring beg (- (point) 2)))
696 (setq thisfilepos (read (current-buffer)))
697 ;; read in version 19 stops at the end of number.
698 ;; Advance to the next line.
699 (forward-line 1)
700 (if (> thisfilepos nodepos)
701 (throw 'foo t))
702 (setq lastfilename thisfilename)
703 (setq lastfilepos thisfilepos))
704 (forward-line 1)))))
705 (setq lastfilename nodepos)
706 (setq lastfilepos 0))
c5fe2ff1
RS
707 ;; Assume previous buffer is in Info-mode.
708 ;; (set-buffer (get-buffer "*info*"))
a384cab3
JB
709 (or (equal Info-current-subfile lastfilename)
710 (let ((buffer-read-only nil))
711 (setq buffer-file-name nil)
712 (widen)
713 (erase-buffer)
1143a6b0 714 (info-insert-file-contents lastfilename)
a384cab3
JB
715 (set-buffer-modified-p nil)
716 (setq Info-current-subfile lastfilename)))
717 (goto-char (point-min))
718 (search-forward "\n\^_")
a361deba
RS
719 (if (numberp nodepos)
720 (+ (- nodepos lastfilepos) (point)))))
a384cab3
JB
721
722;; Select the info node that point is in.
723(defun Info-select-node ()
1bcedb3b
RS
724 ;; Bind this in case the user sets it to nil.
725 (let ((case-fold-search t))
726 (save-excursion
727 ;; Find beginning of node.
728 (search-backward "\n\^_")
729 (forward-line 2)
730 ;; Get nodename spelled as it is in the node.
731 (re-search-forward "Node:[ \t]*")
732 (setq Info-current-node
733 (buffer-substring-no-properties (point)
734 (progn
735 (skip-chars-forward "^,\t\n")
736 (point))))
737 (Info-set-mode-line)
738 ;; Find the end of it, and narrow.
739 (beginning-of-line)
740 (let (active-expression)
741 (narrow-to-region (point)
742 (if (re-search-forward "\n[\^_\f]" nil t)
743 (prog1
744 (1- (point))
745 (if (looking-at "[\n\^_\f]*execute: ")
746 (progn
747 (goto-char (match-end 0))
748 (setq active-expression
749 (read (current-buffer))))))
750 (point-max)))
751 (if Info-enable-active-nodes (eval active-expression))
752 (if Info-fontify (Info-fontify-node))
753 (run-hooks 'Info-selection-hook)))))
a384cab3
JB
754
755(defun Info-set-mode-line ()
756 (setq mode-line-buffer-identification
757 (concat
a580e884 758 " Info: ("
a384cab3
JB
759 (if Info-current-file
760 (file-name-nondirectory Info-current-file)
761 "")
762 ")"
763 (or Info-current-node ""))))
764\f
765;; Go to an info node specified with a filename-and-nodename string
766;; of the sort that is found in pointers in nodes.
767
768(defun Info-goto-node (nodename)
769 "Go to info node named NAME. Give just NODENAME or (FILENAME)NODENAME."
552775bd 770 (interactive (list (Info-read-node-name "Goto node: ")))
a384cab3
JB
771 (let (filename)
772 (string-match "\\s *\\((\\s *\\([^\t)]*\\)\\s *)\\s *\\|\\)\\(.*\\)"
773 nodename)
774 (setq filename (if (= (match-beginning 1) (match-end 1))
775 ""
776 (substring nodename (match-beginning 2) (match-end 2)))
777 nodename (substring nodename (match-beginning 3) (match-end 3)))
778 (let ((trim (string-match "\\s *\\'" filename)))
779 (if trim (setq filename (substring filename 0 trim))))
780 (let ((trim (string-match "\\s *\\'" nodename)))
781 (if trim (setq nodename (substring nodename 0 trim))))
e579232c 782 (if transient-mark-mode (deactivate-mark))
a384cab3
JB
783 (Info-find-node (if (equal filename "") nil filename)
784 (if (equal nodename "") "Top" nodename))))
552775bd 785
ddf89211
RS
786(defvar Info-read-node-completion-table)
787
03524be6 788;; This function is used as the "completion table" while reading a node name.
ddf89211 789;; It does completion using the alist in Info-read-node-completion-table
03524be6
RS
790;; unless STRING starts with an open-paren.
791(defun Info-read-node-name-1 (string predicate code)
792 (let ((no-completion (and (> (length string) 0) (eq (aref string 0) ?\())))
793 (cond ((eq code nil)
794 (if no-completion
795 string
ddf89211 796 (try-completion string Info-read-node-completion-table predicate)))
03524be6
RS
797 ((eq code t)
798 (if no-completion
799 nil
ddf89211 800 (all-completions string Info-read-node-completion-table predicate)))
03524be6
RS
801 ((eq code 'lambda)
802 (if no-completion
803 t
ddf89211 804 (assoc string Info-read-node-completion-table))))))
03524be6 805
552775bd
RS
806(defun Info-read-node-name (prompt &optional default)
807 (let* ((completion-ignore-case t)
ddf89211 808 (Info-read-node-completion-table (Info-build-node-completions))
b6d61ffa 809 (nodename (completing-read prompt 'Info-read-node-name-1 nil t)))
552775bd
RS
810 (if (equal nodename "")
811 (or default
812 (Info-read-node-name prompt))
813 nodename)))
814
815(defun Info-build-node-completions ()
816 (or Info-current-file-completions
1bcedb3b
RS
817 (let ((compl nil)
818 ;; Bind this in case the user sets it to nil.
819 (case-fold-search t))
552775bd
RS
820 (save-excursion
821 (save-restriction
822 (if (marker-buffer Info-tag-table-marker)
c5fe2ff1
RS
823 (let ((marker Info-tag-table-marker))
824 (set-buffer (marker-buffer marker))
cedb118c 825 (widen)
c5fe2ff1 826 (goto-char marker)
552775bd
RS
827 (while (re-search-forward "\nNode: \\(.*\\)\177" nil t)
828 (setq compl
829 (cons (list (buffer-substring (match-beginning 1)
830 (match-end 1)))
831 compl))))
832 (widen)
833 (goto-char (point-min))
834 (while (search-forward "\n\^_" nil t)
835 (forward-line 1)
836 (let ((beg (point)))
837 (forward-line 1)
838 (if (re-search-backward "Node: *\\([^,\n]*\\) *[,\n\t]"
839 beg t)
840 (setq compl
841 (cons (list (buffer-substring (match-beginning 1)
842 (match-end 1)))
843 compl))))))))
844 (setq Info-current-file-completions compl))))
a384cab3 845\f
aea2a8da
JB
846(defun Info-restore-point (hl)
847 "If this node has been visited, restore the point value when we left."
cedb118c
RS
848 (while hl
849 (if (and (equal (nth 0 (car hl)) Info-current-file)
e90d1271
RS
850 ;; Use string-equal, not equal, to ignore text props.
851 (string-equal (nth 1 (car hl)) Info-current-node))
cedb118c 852 (progn
d96504df
KH
853 (goto-char (nth 2 (car hl)))
854 (setq hl nil)) ;terminate the while at next iter
cedb118c 855 (setq hl (cdr hl)))))
aea2a8da 856\f
a384cab3 857(defvar Info-last-search nil
d5913bf6 858 "Default regexp for \\<Info-mode-map>\\[Info-search] command to search for.")
a384cab3
JB
859
860(defun Info-search (regexp)
861 "Search for REGEXP, starting from point, and select node it's found in."
862 (interactive "sSearch (regexp): ")
e579232c 863 (if transient-mark-mode (deactivate-mark))
a384cab3
JB
864 (if (equal regexp "")
865 (setq regexp Info-last-search)
866 (setq Info-last-search regexp))
867 (let ((found ()) current
868 (onode Info-current-node)
869 (ofile Info-current-file)
870 (opoint (point))
a361deba 871 (ostart (window-start))
a384cab3
JB
872 (osubfile Info-current-subfile))
873 (save-excursion
874 (save-restriction
875 (widen)
876 (if (null Info-current-subfile)
877 (progn (re-search-forward regexp) (setq found (point)))
878 (condition-case err
879 (progn (re-search-forward regexp) (setq found (point)))
880 (search-failed nil)))))
881 (if (not found) ;can only happen in subfile case -- else would have erred
882 (unwind-protect
883 (let ((list ()))
c5fe2ff1
RS
884 (save-excursion
885 (set-buffer (marker-buffer Info-tag-table-marker))
a384cab3 886 (goto-char (point-min))
c5fe2ff1
RS
887 (search-forward "\n\^_\nIndirect:")
888 (save-restriction
889 (narrow-to-region (point)
890 (progn (search-forward "\n\^_")
891 (1- (point))))
892 (goto-char (point-min))
893 (search-forward (concat "\n" osubfile ": "))
894 (beginning-of-line)
895 (while (not (eobp))
896 (re-search-forward "\\(^.*\\): [0-9]+$")
897 (goto-char (+ (match-end 1) 2))
898 (setq list (cons (cons (read (current-buffer))
899 (buffer-substring
900 (match-beginning 1) (match-end 1)))
901 list))
902 (goto-char (1+ (match-end 0))))
903 (setq list (nreverse list)
904 current (car (car list))
905 list (cdr list))))
a384cab3
JB
906 (while list
907 (message "Searching subfile %s..." (cdr (car list)))
908 (Info-read-subfile (car (car list)))
909 (setq list (cdr list))
dfbf6104 910;; (goto-char (point-min))
a384cab3
JB
911 (if (re-search-forward regexp nil t)
912 (setq found (point) list ())))
913 (if found
914 (message "")
915 (signal 'search-failed (list regexp))))
916 (if (not found)
a361deba 917 (progn (Info-read-subfile osubfile)
a384cab3 918 (goto-char opoint)
a361deba
RS
919 (Info-select-node)
920 (set-window-start (selected-window) ostart)))))
a384cab3
JB
921 (widen)
922 (goto-char found)
923 (Info-select-node)
e90d1271
RS
924 ;; Use string-equal, not equal, to ignore text props.
925 (or (and (string-equal onode Info-current-node)
a384cab3
JB
926 (equal ofile Info-current-file))
927 (setq Info-history (cons (list ofile onode opoint)
928 Info-history)))))
929\f
930;; Extract the value of the node-pointer named NAME.
931;; If there is none, use ERRORNAME in the error message;
932;; if ERRORNAME is nil, just return nil.
933(defun Info-extract-pointer (name &optional errorname)
1bcedb3b
RS
934 ;; Bind this in case the user sets it to nil.
935 (let ((case-fold-search t))
936 (save-excursion
937 (goto-char (point-min))
938 (forward-line 1)
939 (if (re-search-backward (concat name ":") nil t)
940 (progn
941 (goto-char (match-end 0))
942 (Info-following-node-name))
943 (if (eq errorname t)
944 nil
945 (error "Node has no %s" (capitalize (or errorname name))))))))
a384cab3 946
7ea13762
RS
947;; Return the node name in the buffer following point.
948;; ALLOWEDCHARS, if non-nil, goes within [...] to make a regexp
c5fe2ff1 949;; saying which chars may appear in the node name.
a384cab3
JB
950(defun Info-following-node-name (&optional allowedchars)
951 (skip-chars-forward " \t")
70d78eb6 952 (buffer-substring-no-properties
a384cab3
JB
953 (point)
954 (progn
955 (while (looking-at (concat "[" (or allowedchars "^,\t\n") "]"))
956 (skip-chars-forward (concat (or allowedchars "^,\t\n") "("))
957 (if (looking-at "(")
958 (skip-chars-forward "^)")))
959 (skip-chars-backward " ")
960 (point))))
961
962(defun Info-next ()
963 "Go to the next node of this node."
964 (interactive)
965 (Info-goto-node (Info-extract-pointer "next")))
966
967(defun Info-prev ()
968 "Go to the previous node of this node."
969 (interactive)
970 (Info-goto-node (Info-extract-pointer "prev[ious]*" "previous")))
971
5dab2fb4
RS
972(defun Info-up (&optional same-file)
973 "Go to the superior node of this node.
974If SAME-FILE is non-nil, do not move to a different Info file."
a384cab3 975 (interactive)
5dab2fb4
RS
976 (let ((node (Info-extract-pointer "up")))
977 (and same-file
978 (string-match "^(" node)
979 (error "Up node is in another Info file"))
980 (Info-goto-node node))
aea2a8da 981 (Info-restore-point Info-history))
a384cab3
JB
982
983(defun Info-last ()
984 "Go back to the last node visited."
985 (interactive)
986 (or Info-history
987 (error "This is the first Info node you looked at"))
988 (let (filename nodename opoint)
989 (setq filename (car (car Info-history)))
990 (setq nodename (car (cdr (car Info-history))))
991 (setq opoint (car (cdr (cdr (car Info-history)))))
992 (setq Info-history (cdr Info-history))
993 (Info-find-node filename nodename)
994 (setq Info-history (cdr Info-history))
995 (goto-char opoint)))
996
997(defun Info-directory ()
998 "Go to the Info directory node."
999 (interactive)
1000 (Info-find-node "dir" "top"))
1001\f
1002(defun Info-follow-reference (footnotename)
1003 "Follow cross reference named NAME to the node it refers to.
1004NAME may be an abbreviation of the reference name."
1005 (interactive
1006 (let ((completion-ignore-case t)
1bcedb3b 1007 (case-fold-search t)
67bc89ab 1008 completions default alt-default (start-point (point)) str i bol eol)
a384cab3 1009 (save-excursion
67bc89ab
RS
1010 ;; Store end and beginning of line.
1011 (end-of-line)
1012 (setq eol (point))
1013 (beginning-of-line)
1014 (setq bol (point))
1015
a384cab3
JB
1016 (goto-char (point-min))
1017 (while (re-search-forward "\\*note[ \n\t]*\\([^:]*\\):" nil t)
1018 (setq str (buffer-substring
1019 (match-beginning 1)
1020 (1- (point))))
1021 ;; See if this one should be the default.
1022 (and (null default)
1f179e27 1023 (<= (match-beginning 0) start-point)
a384cab3
JB
1024 (<= start-point (point))
1025 (setq default t))
67bc89ab
RS
1026 ;; See if this one should be the alternate default.
1027 (and (null alt-default)
1028 (and (<= bol (match-beginning 0))
1029 (<= (point) eol))
1030 (setq alt-default t))
a384cab3
JB
1031 (setq i 0)
1032 (while (setq i (string-match "[ \n\t]+" str i))
1033 (setq str (concat (substring str 0 i) " "
1034 (substring str (match-end 0))))
1035 (setq i (1+ i)))
1036 ;; Record as a completion and perhaps as default.
1037 (if (eq default t) (setq default str))
67bc89ab 1038 (if (eq alt-default t) (setq alt-default str))
ec6d29af
KH
1039 ;; Don't add this string if it's a duplicate.
1040 ;; We use a loop instead of "(assoc str completions)" because
1041 ;; we want to do a case-insensitive compare.
1042 (let ((tail completions)
1043 (tem (downcase str)))
1044 (while (and tail
1045 (not (string-equal tem (downcase (car (car tail))))))
1046 (setq tail (cdr tail)))
1047 (or tail
1048 (setq completions
1049 (cons (cons str nil)
1050 completions))))))
67bc89ab
RS
1051 ;; If no good default was found, try an alternate.
1052 (or default
1053 (setq default alt-default))
1054 ;; If only one cross-reference found, then make it default.
1055 (if (eq (length completions) 1)
1056 (setq default (car (car completions))))
a384cab3 1057 (if completions
b0ebdfe5
RS
1058 (let ((input (completing-read (if default
1059 (concat "Follow reference named: ("
1060 default ") ")
1061 "Follow reference named: ")
1062 completions nil t)))
1063 (list (if (equal input "")
1064 default input)))
a384cab3 1065 (error "No cross-references in this node"))))
1bcedb3b
RS
1066 (let (target beg i (str (concat "\\*note " (regexp-quote footnotename)))
1067 (case-fold-search t))
a384cab3
JB
1068 (while (setq i (string-match " " str i))
1069 (setq str (concat (substring str 0 i) "[ \t\n]+" (substring str (1+ i))))
1070 (setq i (+ i 6)))
1071 (save-excursion
1072 (goto-char (point-min))
1073 (or (re-search-forward str nil t)
1074 (error "No cross-reference named %s" footnotename))
1075 (goto-char (+ (match-beginning 0) 5))
1076 (setq target
1077 (Info-extract-menu-node-name "Bad format cross reference" t)))
1078 (while (setq i (string-match "[ \t\n]+" target i))
1079 (setq target (concat (substring target 0 i) " "
1080 (substring target (match-end 0))))
1081 (setq i (+ i 1)))
1082 (Info-goto-node target)))
1083
1084(defun Info-extract-menu-node-name (&optional errmessage multi-line)
1085 (skip-chars-forward " \t\n")
1086 (let ((beg (point))
1087 str i)
1088 (skip-chars-forward "^:")
1089 (forward-char 1)
1090 (setq str
1091 (if (looking-at ":")
70d78eb6 1092 (buffer-substring-no-properties beg (1- (point)))
a384cab3
JB
1093 (skip-chars-forward " \t\n")
1094 (Info-following-node-name (if multi-line "^.,\t" "^.,\t\n"))))
1095 (while (setq i (string-match "\n" str i))
1096 (aset str i ?\ ))
2e52ff59
RS
1097 ;; Collapse multiple spaces.
1098 (while (string-match " +" str)
1099 (setq str (replace-match " " t t str)))
a384cab3
JB
1100 str))
1101
e8adde3f 1102;; No one calls this.
9e5c2f50
RS
1103;;(defun Info-menu-item-sequence (list)
1104;; (while list
e8adde3f 1105;; (Info-menu (car list))
9e5c2f50 1106;; (setq list (cdr list))))
a384cab3 1107
6356e646
RS
1108(defvar Info-complete-menu-buffer)
1109
e8adde3f
RS
1110(defun Info-complete-menu-item (string predicate action)
1111 (let ((case-fold-search t))
1112 (cond ((eq action nil)
1113 (let (completions
b37daea4 1114 (pattern (concat "\n\\* +\\("
e8adde3f
RS
1115 (regexp-quote string)
1116 "[^:\t\n]*\\):")))
1117 (save-excursion
1118 (set-buffer Info-complete-menu-buffer)
1119 (goto-char (point-min))
763da090 1120 (search-forward "\n* Menu:")
e8adde3f
RS
1121 (while (re-search-forward pattern nil t)
1122 (setq completions (cons (cons (format "%s"
1123 (buffer-substring
1124 (match-beginning 1)
1125 (match-end 1)))
1126 (match-beginning 1))
1127 completions))))
1128 (try-completion string completions predicate)))
1129 ((eq action t)
1130 (let (completions
b37daea4 1131 (pattern (concat "\n\\* +\\("
e8adde3f
RS
1132 (regexp-quote string)
1133 "[^:\t\n]*\\):")))
1134 (save-excursion
1135 (set-buffer Info-complete-menu-buffer)
1136 (goto-char (point-min))
763da090 1137 (search-forward "\n* Menu:")
e8adde3f
RS
1138 (while (re-search-forward pattern nil t)
1139 (setq completions (cons (cons (format "%s"
1140 (buffer-substring
1141 (match-beginning 1)
1142 (match-end 1)))
1143 (match-beginning 1))
1144 completions))))
1145 (all-completions string completions predicate)))
1146 (t
1147 (save-excursion
1148 (set-buffer Info-complete-menu-buffer)
1149 (goto-char (point-min))
763da090 1150 (search-forward "\n* Menu:")
b37daea4 1151 (re-search-forward (concat "\n\\* +"
e8adde3f
RS
1152 (regexp-quote string)
1153 ":")
1154 nil t))))))
1155
1156
a384cab3
JB
1157(defun Info-menu (menu-item)
1158 "Go to node for menu item named (or abbreviated) NAME.
1159Completion is allowed, and the menu item point is on is the default."
1160 (interactive
1161 (let ((completions '())
1162 ;; If point is within a menu item, use that item as the default
1163 (default nil)
1164 (p (point))
211d6309 1165 beg
a384cab3
JB
1166 (last nil))
1167 (save-excursion
1168 (goto-char (point-min))
1169 (if (not (search-forward "\n* menu:" nil t))
1170 (error "No menu in this node"))
e8adde3f
RS
1171 (setq beg (point))
1172 (and (< (point) p)
1173 (save-excursion
1174 (goto-char p)
1175 (end-of-line)
b37daea4 1176 (re-search-backward "\n\\* +\\([^:\t\n]*\\):" beg t)
e8adde3f
RS
1177 (setq default (format "%s" (buffer-substring
1178 (match-beginning 1)
1179 (match-end 1)))))))
a384cab3
JB
1180 (let ((item nil))
1181 (while (null item)
e8adde3f
RS
1182 (setq item (let ((completion-ignore-case t)
1183 (Info-complete-menu-buffer (current-buffer)))
a384cab3
JB
1184 (completing-read (if default
1185 (format "Menu item (default %s): "
1186 default)
1187 "Menu item: ")
e8adde3f 1188 'Info-complete-menu-item nil t)))
aea2a8da
JB
1189 ;; we rely on the fact that completing-read accepts an input
1190 ;; of "" even when the require-match argument is true and ""
1191 ;; is not a valid possibility
a384cab3
JB
1192 (if (string= item "")
1193 (if default
1194 (setq item default)
1195 ;; ask again
1196 (setq item nil))))
1197 (list item))))
1198 ;; there is a problem here in that if several menu items have the same
1199 ;; name you can only go to the node of the first with this command.
1200 (Info-goto-node (Info-extract-menu-item menu-item)))
1201
1202(defun Info-extract-menu-item (menu-item)
1203 (setq menu-item (regexp-quote menu-item))
1bcedb3b
RS
1204 (let ((case-fold-search t))
1205 (save-excursion
1206 (goto-char (point-min))
1207 (or (search-forward "\n* menu:" nil t)
1208 (error "No menu in this node"))
1209 (or (re-search-forward (concat "\n\\* +" menu-item ":") nil t)
1210 (re-search-forward (concat "\n\\* +" menu-item) nil t)
1211 (error "No such item in menu"))
1212 (beginning-of-line)
1213 (forward-char 2)
1214 (Info-extract-menu-node-name))))
a384cab3
JB
1215
1216;; If COUNT is nil, use the last item in the menu.
1217(defun Info-extract-menu-counting (count)
1bcedb3b
RS
1218 (let ((case-fold-search t))
1219 (save-excursion
1220 (goto-char (point-min))
1221 (or (search-forward "\n* menu:" nil t)
1222 (error "No menu in this node"))
1223 (if count
1224 (or (search-forward "\n* " nil t count)
1225 (error "Too few items in menu"))
1226 (while (search-forward "\n* " nil t)
1227 nil))
1228 (Info-extract-menu-node-name))))
a384cab3 1229
e38e7367
RM
1230(defun Info-nth-menu-item ()
1231 "Go to the node of the Nth menu item.
1232N is the digit argument used to invoke this command."
a384cab3 1233 (interactive)
e38e7367
RM
1234 (Info-goto-node
1235 (Info-extract-menu-counting
1236 (- (aref (this-command-keys) (1- (length (this-command-keys)))) ?0))))
a384cab3
JB
1237
1238(defun Info-top-node ()
1239 "Go to the Top node of this file."
1240 (interactive)
1241 (Info-goto-node "Top"))
1242
1243(defun Info-final-node ()
1244 "Go to the final node in this file."
1245 (interactive)
1246 (Info-goto-node "Top")
1247 (let (Info-history)
1248 ;; Go to the last node in the menu of Top.
1249 (Info-goto-node (Info-extract-menu-counting nil))
1250 ;; If the last node in the menu is not last in pointer structure,
1251 ;; move forward until we can't go any farther.
1252 (while (Info-forward-node t t) nil)
1253 ;; Then keep moving down to last subnode, unless we reach an index.
1254 (while (and (not (string-match "\\<index\\>" Info-current-node))
1255 (save-excursion (search-forward "\n* Menu:" nil t)))
1256 (Info-goto-node (Info-extract-menu-counting nil)))))
1257
1258(defun Info-forward-node (&optional not-down no-error)
1259 "Go forward one node, considering all nodes as forming one sequence."
1260 (interactive)
1261 (goto-char (point-min))
1262 (forward-line 1)
1263 ;; three possibilities, in order of priority:
1264 ;; 1. next node is in a menu in this node (but not in an index)
1265 ;; 2. next node is next at same level
1266 ;; 3. next node is up and next
1267 (cond ((and (not not-down)
1268 (save-excursion (search-forward "\n* menu:" nil t))
1269 (not (string-match "\\<index\\>" Info-current-node)))
463f48f4 1270 (Info-goto-node (Info-extract-menu-counting 1))
a384cab3
JB
1271 t)
1272 ((save-excursion (search-backward "next:" nil t))
1273 (Info-next)
1274 t)
1275 ((and (save-excursion (search-backward "up:" nil t))
e90d1271
RS
1276 ;; Use string-equal, not equal, to ignore text props.
1277 (not (string-equal (downcase (Info-extract-pointer "up"))
1278 "top")))
a384cab3
JB
1279 (let ((old-node Info-current-node))
1280 (Info-up)
1281 (let (Info-history success)
1282 (unwind-protect
1283 (setq success (Info-forward-node t no-error))
1284 (or success (Info-goto-node old-node))))))
1285 (no-error nil)
1286 (t (error "No pointer forward from this node"))))
1287
1288(defun Info-backward-node ()
1289 "Go backward one node, considering all nodes as forming one sequence."
1290 (interactive)
1291 (let ((prevnode (Info-extract-pointer "prev[ious]*" t))
1292 (upnode (Info-extract-pointer "up" t)))
1293 (cond ((and upnode (string-match "(" upnode))
1294 (error "First node in file"))
1295 ((and upnode (or (null prevnode)
e90d1271
RS
1296 ;; Use string-equal, not equal,
1297 ;; to ignore text properties.
1298 (string-equal (downcase prevnode)
1299 (downcase upnode))))
a384cab3
JB
1300 (Info-up))
1301 (prevnode
1302 ;; If we move back at the same level,
1303 ;; go down to find the last subnode*.
1304 (Info-prev)
1305 (let (Info-history)
1306 (while (and (not (string-match "\\<index\\>" Info-current-node))
1307 (save-excursion (search-forward "\n* Menu:" nil t)))
1308 (Info-goto-node (Info-extract-menu-counting nil)))))
1309 (t
1310 (error "No pointer backward from this node")))))
1311
1312(defun Info-exit ()
1313 "Exit Info by selecting some other buffer."
1314 (interactive)
552775bd
RS
1315 (if Info-standalone
1316 (save-buffers-kill-emacs)
4643e92f 1317 (quit-window)))
a384cab3 1318
253db917
ER
1319(defun Info-next-menu-item ()
1320 (interactive)
1321 (save-excursion
1322 (forward-line -1)
1323 (search-forward "\n* menu:" nil t)
1324 (or (search-forward "\n* " nil t)
1325 (error "No more items in menu"))
1326 (Info-goto-node (Info-extract-menu-node-name))))
1327
1328(defun Info-last-menu-item ()
1329 (interactive)
1330 (save-excursion
1331 (forward-line 1)
0a56332b
RS
1332 (let ((beg (save-excursion
1333 (and (search-backward "\n* menu:" nil t)
1334 (point)))))
1335 (or (and beg (search-backward "\n* " beg t))
1336 (error "No previous items in menu")))
1337 (Info-goto-node (save-excursion
1338 (goto-char (match-end 0))
1339 (Info-extract-menu-node-name)))))
253db917 1340
552775bd 1341(defmacro Info-no-error (&rest body)
253db917
ER
1342 (list 'condition-case nil (cons 'progn (append body '(t))) '(error nil)))
1343
1344(defun Info-next-preorder ()
3c9179ea
RS
1345 "Go to the next subnode or the next node, or go up a level."
1346 (interactive)
1347 (cond ((Info-no-error (Info-next-menu-item)))
1348 ((Info-no-error (Info-next)))
5dab2fb4 1349 ((Info-no-error (Info-up t))
ed690657
KH
1350 ;; Since we have already gone thru all the items in this menu,
1351 ;; go up to the end of this node.
b54d0f0e
RS
1352 (goto-char (point-max))
1353 ;; Since logically we are done with the node with that menu,
1354 ;; move on from it.
1355 (Info-next-preorder))
3c9179ea
RS
1356 (t
1357 (error "No more nodes"))))
253db917
ER
1358
1359(defun Info-last-preorder ()
1360 "Go to the last node, popping up a level if there is none."
1361 (interactive)
0a56332b
RS
1362 (cond ((Info-no-error
1363 (Info-last-menu-item)
1364 ;; If we go down a menu item, go to the end of the node
1365 ;; so we can scroll back through it.
ed690657 1366 (goto-char (point-max)))
b54d0f0e
RS
1367 ;; Keep going down, as long as there are nested menu nodes.
1368 (while (Info-no-error
1369 (Info-last-menu-item)
1370 ;; If we go down a menu item, go to the end of the node
1371 ;; so we can scroll back through it.
1372 (goto-char (point-max))))
ed690657 1373 (recenter -1))
5dab2fb4
RS
1374 ((and (not (equal (Info-extract-pointer "up")
1375 (Info-extract-pointer "prev"))))
1376 (Info-no-error (Info-prev))
ed690657 1377 (goto-char (point-max))
b54d0f0e
RS
1378 (while (Info-no-error
1379 (Info-last-menu-item)
1380 ;; If we go down a menu item, go to the end of the node
1381 ;; so we can scroll back through it.
1382 (goto-char (point-max))))
ed690657 1383 (recenter -1))
5dab2fb4 1384 ((Info-no-error (Info-up t))
ed690657
KH
1385 (goto-char (point-min))
1386 (or (search-forward "\n* Menu:" nil t)
1387 (goto-char (point-max))))
1388 (t (error "No previous nodes"))))
253db917
ER
1389
1390(defun Info-scroll-up ()
3f32dc86 1391 "Scroll one screenful forward in Info, considering all nodes as one sequence.
280d11ed
RS
1392Once you scroll far enough in a node that its menu appears on the screen
1393but after point, the next scroll moves into its first subnode.
1394
1395When you scroll past the end of a node, that goes to the next node; if
1396this node has no successor, it moves to the parent node's successor,
1397and so on. If point is inside the menu of a node, it moves to
1398subnode indicated by the following menu item. (That case won't
1399normally result from this command, but can happen in other ways.)"
1400
253db917 1401 (interactive)
0a56332b
RS
1402 (if (or (< (window-start) (point-min))
1403 (> (window-start) (point-max)))
1404 (set-window-start (selected-window) (point)))
1405 (let ((virtual-end (save-excursion
1406 (goto-char (point-min))
1407 (if (search-forward "\n* Menu:" nil t)
1408 (point)
1409 (point-max)))))
1410 (if (or (< virtual-end (window-start))
1411 (pos-visible-in-window-p virtual-end))
1412 (Info-next-preorder)
1413 (scroll-up))))
253db917
ER
1414
1415(defun Info-scroll-down ()
3f32dc86 1416 "Scroll one screenful back in Info, considering all nodes as one sequence.
ed690657
KH
1417Within the menu of a node, this goes to its last subnode.
1418When you scroll past the beginning of a node, that goes to the
1419previous node or back up to the parent node."
253db917 1420 (interactive)
0a56332b
RS
1421 (if (or (< (window-start) (point-min))
1422 (> (window-start) (point-max)))
1423 (set-window-start (selected-window) (point)))
b54d0f0e
RS
1424 (let* ((current-point (point))
1425 (virtual-end (save-excursion
1426 (beginning-of-line)
1427 (setq current-point (point))
1428 (goto-char (point-min))
1429 (search-forward "\n* Menu:"
1430 current-point
1431 t))))
0a56332b
RS
1432 (if (or virtual-end (pos-visible-in-window-p (point-min)))
1433 (Info-last-preorder)
1434 (scroll-down))))
253db917 1435
56cda6f5 1436(defun Info-next-reference (&optional recur)
552775bd
RS
1437 "Move cursor to the next cross-reference or menu item in the node."
1438 (interactive)
1439 (let ((pat "\\*note[ \n\t]*\\([^:]*\\):\\|^\\* .*:")
1bcedb3b
RS
1440 (old-pt (point))
1441 (case-fold-search t))
552775bd
RS
1442 (or (eobp) (forward-char 1))
1443 (or (re-search-forward pat nil t)
1444 (progn
1445 (goto-char (point-min))
1446 (or (re-search-forward pat nil t)
1447 (progn
1448 (goto-char old-pt)
1449 (error "No cross references in this node")))))
1450 (goto-char (match-beginning 0))
1451 (if (looking-at "\\* Menu:")
56cda6f5
RS
1452 (if recur
1453 (error "No cross references in this node")
1454 (Info-next-reference t)))))
552775bd 1455
56cda6f5 1456(defun Info-prev-reference (&optional recur)
552775bd
RS
1457 "Move cursor to the previous cross-reference or menu item in the node."
1458 (interactive)
1459 (let ((pat "\\*note[ \n\t]*\\([^:]*\\):\\|^\\* .*:")
1bcedb3b
RS
1460 (old-pt (point))
1461 (case-fold-search t))
552775bd
RS
1462 (or (re-search-backward pat nil t)
1463 (progn
1464 (goto-char (point-max))
1465 (or (re-search-backward pat nil t)
1466 (progn
1467 (goto-char old-pt)
1468 (error "No cross references in this node")))))
1469 (goto-char (match-beginning 0))
1470 (if (looking-at "\\* Menu:")
56cda6f5
RS
1471 (if recur
1472 (error "No cross references in this node")
1473 (Info-prev-reference t)))))
552775bd 1474
1143a6b0
ER
1475(defun Info-index (topic)
1476 "Look up a string in the index for this file.
1477The index is defined as the first node in the top-level menu whose
1478name contains the word \"Index\", plus any immediately following
1479nodes whose names also contain the word \"Index\".
1480If there are no exact matches to the specified topic, this chooses
1481the first match which is a case-insensitive substring of a topic.
1482Use the `,' command to see the other matches.
1483Give a blank topic name to go to the Index node itself."
1484 (interactive "sIndex topic: ")
1485 (let ((orignode Info-current-node)
1486 (rnode nil)
b37daea4 1487 (pattern (format "\n\\* +\\([^\n:]*%s[^\n:]*\\):[ \t]*\\([^.\n]*\\)\\.[ \t]*\\([0-9]*\\)"
1143a6b0 1488 (regexp-quote topic)))
1bcedb3b
RS
1489 node
1490 (case-fold-search t))
1143a6b0
ER
1491 (Info-goto-node "Top")
1492 (or (search-forward "\n* menu:" nil t)
1493 (error "No index"))
1494 (or (re-search-forward "\n\\* \\(.*\\<Index\\>\\)" nil t)
1495 (error "No index"))
1496 (goto-char (match-beginning 1))
c696ac76
RS
1497 ;; Here, and subsequently in this function,
1498 ;; we bind Info-history to nil for internal node-switches
1499 ;; so that we don't put junk in the history.
1500 ;; In the first Info-goto-node call, above, we do update the history
1501 ;; because that is what the user's previous node choice into it.
1502 (let ((Info-history nil))
1143a6b0
ER
1503 (Info-goto-node (Info-extract-menu-node-name)))
1504 (or (equal topic "")
1505 (let ((matches nil)
1506 (exact nil)
c696ac76 1507 (Info-history nil)
1143a6b0
ER
1508 found)
1509 (while
1510 (progn
1511 (goto-char (point-min))
1512 (while (re-search-forward pattern nil t)
1513 (setq matches
1514 (cons (list (buffer-substring (match-beginning 1)
1515 (match-end 1))
1516 (buffer-substring (match-beginning 2)
1517 (match-end 2))
1518 Info-current-node
1519 (string-to-int (concat "0"
1520 (buffer-substring
1521 (match-beginning 3)
1522 (match-end 3)))))
1523 matches)))
1524 (and (setq node (Info-extract-pointer "next" t))
1525 (string-match "\\<Index\\>" node)))
1526 (Info-goto-node node))
1527 (or matches
1528 (progn
81e14cb2 1529 (Info-goto-node orignode)
920bdaab 1530 (error "No `%s' in index" topic)))
1143a6b0
ER
1531 ;; Here it is a feature that assoc is case-sensitive.
1532 (while (setq found (assoc topic matches))
1533 (setq exact (cons found exact)
1534 matches (delq found matches)))
1535 (setq Info-index-alternatives (nconc exact (nreverse matches)))
1536 (Info-index-next 0)))))
1537
1538(defun Info-index-next (num)
1539 "Go to the next matching index item from the last `i' command."
1540 (interactive "p")
1541 (or Info-index-alternatives
882e61bf 1542 (error "No previous `i' command"))
1143a6b0
ER
1543 (while (< num 0)
1544 (setq num (+ num (length Info-index-alternatives))))
1545 (while (> num 0)
1546 (setq Info-index-alternatives
1547 (nconc (cdr Info-index-alternatives)
1548 (list (car Info-index-alternatives)))
1549 num (1- num)))
1550 (Info-goto-node (nth 1 (car Info-index-alternatives)))
1551 (if (> (nth 3 (car Info-index-alternatives)) 0)
1552 (forward-line (nth 3 (car Info-index-alternatives)))
1553 (forward-line 3) ; don't search in headers
1554 (let ((name (car (car Info-index-alternatives))))
920bdaab
RS
1555 (Info-find-index-name name)))
1556 (message "Found `%s' in %s. %s"
1143a6b0
ER
1557 (car (car Info-index-alternatives))
1558 (nth 2 (car Info-index-alternatives))
1559 (if (cdr Info-index-alternatives)
1560 "(Press `,' for more)"
1561 "(Only match)")))
1562
920bdaab
RS
1563(defun Info-find-index-name (name)
1564 "Move point to the place within the current node where NAME is defined."
1bcedb3b
RS
1565 (let ((case-fold-search t))
1566 (if (or (re-search-forward (format
1567 "[a-zA-Z]+: %s\\( \\|$\\)"
1568 (regexp-quote name)) nil t)
1569 (search-forward (format "`%s'" name) nil t)
1570 (and (string-match "\\`.*\\( (.*)\\)\\'" name)
1571 (search-forward
1572 (format "`%s'" (substring name 0 (match-beginning 1)))
1573 nil t))
1574 (search-forward name nil t))
1575 (beginning-of-line)
1576 (goto-char (point-min)))))
920bdaab 1577
a384cab3
JB
1578(defun Info-undefined ()
1579 "Make command be undefined in Info."
1580 (interactive)
1581 (ding))
1582
1583(defun Info-help ()
1584 "Enter the Info tutorial."
1585 (interactive)
1586 (delete-other-windows)
1587 (Info-find-node "info"
1588 (if (< (window-height) 23)
1589 "Help-Small-Screen"
1590 "Help")))
1591
1592(defun Info-summary ()
1593 "Display a brief summary of all Info commands."
1594 (interactive)
1595 (save-window-excursion
1596 (switch-to-buffer "*Help*")
881c84c7 1597 (setq buffer-read-only nil)
a384cab3
JB
1598 (erase-buffer)
1599 (insert (documentation 'Info-mode))
9d29f94c 1600 (help-mode)
a384cab3
JB
1601 (goto-char (point-min))
1602 (let (ch flag)
1603 (while (progn (setq flag (not (pos-visible-in-window-p (point-max))))
1604 (message (if flag "Type Space to see more"
1605 "Type Space to return to Info"))
1614c867 1606 (if (not (eq ?\ (setq ch (read-event))))
dbc4e1c1 1607 (progn (setq unread-command-events (list ch)) nil)
a384cab3 1608 flag))
552775bd
RS
1609 (scroll-up)))
1610 (bury-buffer "*Help*")))
a384cab3
JB
1611\f
1612(defun Info-get-token (pos start all &optional errorstring)
1613 "Return the token around POS,
1614POS must be somewhere inside the token
1615START is a regular expression which will match the
1616 beginning of the tokens delimited string
1617ALL is a regular expression with a single
90a715f0 1618 parenthesized subpattern which is the token to be
a384cab3
JB
1619 returned. E.g. '{\(.*\)}' would return any string
1620 enclosed in braces around POS.
1621SIG optional fourth argument, controls action on no match
1622 nil: return nil
1623 t: beep
1624 a string: signal an error, using that string."
1bcedb3b
RS
1625 (let ((case-fold-search t))
1626 (save-excursion
1627 (goto-char pos)
1628 ;; First look for a match for START that goes across POS.
1629 (while (and (not (bobp)) (> (point) (- pos (length start)))
1630 (not (looking-at start)))
1631 (forward-char -1))
1632 ;; If we did not find one, search back for START
1633 ;; (this finds only matches that end at or before POS).
1634 (or (looking-at start)
1635 (progn
1636 (goto-char pos)
1637 (re-search-backward start (max (point-min) (- pos 200)) 'yes)))
1638 (let (found)
1639 (while (and (re-search-forward all (min (point-max) (+ pos 200)) 'yes)
1640 (not (setq found (and (<= (match-beginning 0) pos)
1641 (> (match-end 0) pos))))))
1642 (if (and found (<= (match-beginning 0) pos)
1643 (> (match-end 0) pos))
1644 (buffer-substring (match-beginning 1) (match-end 1))
1645 (cond ((null errorstring)
1646 nil)
1647 ((eq errorstring t)
1648 (beep)
1649 nil)
1650 (t
1651 (error "No %s around position %d" errorstring pos))))))))
a384cab3 1652
981947af 1653(defun Info-mouse-follow-nearest-node (click)
f9969361
RS
1654 "\\<Info-mode-map>Follow a node reference near point.
1655Like \\[Info-menu], \\[Info-follow-reference], \\[Info-next], \\[Info-prev] or \\[Info-up] command, depending on where you click.
981947af 1656At end of the node's text, moves to the next node, or up if none."
f9969361 1657 (interactive "e")
9e5c2f50
RS
1658 (let* ((start (event-start click))
1659 (window (car start))
1660 (pos (car (cdr start))))
1661 (select-window window)
1662 (goto-char pos))
981947af
KH
1663 (and (not (Info-try-follow-nearest-node))
1664 (save-excursion (forward-line 1) (eobp))
ed690657 1665 (Info-next-preorder)))
981947af
KH
1666
1667(defun Info-follow-nearest-node ()
1668 "\\<Info-mode-map>Follow a node reference near point.
1669Like \\[Info-menu], \\[Info-follow-reference], \\[Info-next], \\[Info-prev] or \\[Info-up] command, depending on where point is.
1670If no reference to follow, moves to the next node, or up if none."
1671 (interactive)
1672 (or (Info-try-follow-nearest-node)
ed690657 1673 (Info-next-preorder)))
981947af
KH
1674
1675;; Common subroutine.
1676(defun Info-try-follow-nearest-node ()
1677 "Follow a node reference near point. Return non-nil if successful."
a384cab3
JB
1678 (let (node)
1679 (cond
981947af
KH
1680 ((setq node (Info-get-token (point) "\\*note[ \n]"
1681 "\\*note[ \n]\\([^:]*\\):"))
a384cab3 1682 (Info-follow-reference node))
b37daea4 1683 ((setq node (Info-get-token (point) "\\* +" "\\* +\\([^:]*\\)::"))
a384cab3 1684 (Info-goto-node node))
631ba13e
RS
1685 ((Info-get-token (point) "\\* +" "\\* +\\([^:]*\\):")
1686 (beginning-of-line)
1687 (forward-char 2)
1688 (setq node (Info-extract-menu-node-name))
1689 (Info-goto-node node))
bc2ada62 1690 ((setq node (Info-get-token (point) "Up: " "Up: \\([^,\n\t]*\\)"))
a384cab3 1691 (Info-goto-node node))
bc2ada62 1692 ((setq node (Info-get-token (point) "Next: " "Next: \\([^,\n\t]*\\)"))
a384cab3 1693 (Info-goto-node node))
bc2ada62 1694 ((setq node (Info-get-token (point) "File: " "File: \\([^,\n\t]*\\)"))
a384cab3 1695 (Info-goto-node "Top"))
bc2ada62 1696 ((setq node (Info-get-token (point) "Prev: " "Prev: \\([^,\n\t]*\\)"))
981947af
KH
1697 (Info-goto-node node)))
1698 node))
a384cab3
JB
1699\f
1700(defvar Info-mode-map nil
1701 "Keymap containing Info commands.")
1702(if Info-mode-map
1703 nil
1704 (setq Info-mode-map (make-keymap))
1705 (suppress-keymap Info-mode-map)
1706 (define-key Info-mode-map "." 'beginning-of-buffer)
253db917 1707 (define-key Info-mode-map " " 'Info-scroll-up)
981947af 1708 (define-key Info-mode-map "\C-m" 'Info-follow-nearest-node)
552775bd
RS
1709 (define-key Info-mode-map "\t" 'Info-next-reference)
1710 (define-key Info-mode-map "\e\t" 'Info-prev-reference)
e38e7367
RM
1711 (define-key Info-mode-map "1" 'Info-nth-menu-item)
1712 (define-key Info-mode-map "2" 'Info-nth-menu-item)
1713 (define-key Info-mode-map "3" 'Info-nth-menu-item)
1714 (define-key Info-mode-map "4" 'Info-nth-menu-item)
1715 (define-key Info-mode-map "5" 'Info-nth-menu-item)
1716 (define-key Info-mode-map "6" 'Info-nth-menu-item)
1717 (define-key Info-mode-map "7" 'Info-nth-menu-item)
1718 (define-key Info-mode-map "8" 'Info-nth-menu-item)
1719 (define-key Info-mode-map "9" 'Info-nth-menu-item)
82a4c008 1720 (define-key Info-mode-map "0" 'undefined)
a384cab3
JB
1721 (define-key Info-mode-map "?" 'Info-summary)
1722 (define-key Info-mode-map "]" 'Info-forward-node)
1723 (define-key Info-mode-map "[" 'Info-backward-node)
1724 (define-key Info-mode-map "<" 'Info-top-node)
1725 (define-key Info-mode-map ">" 'Info-final-node)
1726 (define-key Info-mode-map "b" 'beginning-of-buffer)
1727 (define-key Info-mode-map "d" 'Info-directory)
1728 (define-key Info-mode-map "e" 'Info-edit)
1729 (define-key Info-mode-map "f" 'Info-follow-reference)
1730 (define-key Info-mode-map "g" 'Info-goto-node)
1731 (define-key Info-mode-map "h" 'Info-help)
1143a6b0 1732 (define-key Info-mode-map "i" 'Info-index)
a384cab3
JB
1733 (define-key Info-mode-map "l" 'Info-last)
1734 (define-key Info-mode-map "m" 'Info-menu)
1735 (define-key Info-mode-map "n" 'Info-next)
1736 (define-key Info-mode-map "p" 'Info-prev)
1737 (define-key Info-mode-map "q" 'Info-exit)
1738 (define-key Info-mode-map "s" 'Info-search)
47fc33ca
RS
1739 ;; For consistency with Rmail.
1740 (define-key Info-mode-map "\M-s" 'Info-search)
db0c9809 1741 (define-key Info-mode-map "t" 'Info-top-node)
a384cab3 1742 (define-key Info-mode-map "u" 'Info-up)
1143a6b0 1743 (define-key Info-mode-map "," 'Info-index-next)
803eaf50 1744 (define-key Info-mode-map "\177" 'Info-scroll-down)
981947af 1745 (define-key Info-mode-map [mouse-2] 'Info-mouse-follow-nearest-node)
aea2a8da 1746 )
75a209d4
RS
1747
1748(defun Info-check-pointer (item)
1749 ;; Non-nil if ITEM is present in this node.
1750 (condition-case nil
1751 (Info-extract-pointer item)
1752 (error nil)))
1753
1754(easy-menu-define Info-mode-menu Info-mode-map
1755 "Menu for info files."
1756 '("Info"
1757 ["Up" Info-up (Info-check-pointer "up")]
1758 ["Next" Info-next (Info-check-pointer "next")]
1759 ["Previous" Info-prev (Info-check-pointer "prev[ious]*")]
1760 ("Menu item" ["You should never see this" report-emacs-bug t])
1761 ("Reference" ["You should never see this" report-emacs-bug t])
1762 ["Search..." Info-search t]
1763 ["Goto node..." Info-goto-node t]
1764 ["Last" Info-last Info-history]
1765 ["Exit" Info-exit t]))
1766
1767(defvar Info-menu-last-node nil)
1768;; Last node the menu was created for.
6d2c8e3e 1769;; Value is a list, (FILE-NAME NODE-NAME).
75a209d4
RS
1770
1771(defun Info-menu-update ()
1772 ;; Update the Info menu for the current node.
1773 (condition-case nil
1774 (if (or (not (eq major-mode 'Info-mode))
6d2c8e3e
RS
1775 (equal (list Info-current-file Info-current-node)
1776 Info-menu-last-node))
75a209d4
RS
1777 ()
1778 ;; Update menu menu.
1779 (let* ((Info-complete-menu-buffer (current-buffer))
1780 (items (nreverse (condition-case nil
1781 (Info-complete-menu-item
1782 "" (lambda (e) t) t)
1783 (error nil))))
1784 entries current
1785 (number 0))
1786 (while (and items (< number 9))
1787 (setq current (car items)
1788 items (cdr items)
1789 number (1+ number))
1790 (setq entries (cons `[,current
1791 (Info-menu ,current)
1792 :keys ,(format "%d" number)]
1793 entries)))
1794 (if items
1795 (setq entries (cons ["Other..." Info-menu t] entries)))
1796 (or entries
1797 (setq entries (list ["No menu" nil nil])))
1798 (easy-menu-change '("Info") "Menu item" (nreverse entries)))
1799 ;; Update reference menu. Code stolen from `Info-follow-reference'.
1800 (let ((items nil)
1801 str i entries current
1bcedb3b
RS
1802 (number 0)
1803 (case-fold-search t))
75a209d4
RS
1804 (save-excursion
1805 (goto-char (point-min))
1806 (while (re-search-forward "\\*note[ \n\t]*\\([^:]*\\):" nil t)
1807 (setq str (buffer-substring
1808 (match-beginning 1)
1809 (1- (point))))
1810 (setq i 0)
1811 (while (setq i (string-match "[ \n\t]+" str i))
1812 (setq str (concat (substring str 0 i) " "
1813 (substring str (match-end 0))))
1814 (setq i (1+ i)))
1815 (setq items
1816 (cons str items))))
1817 (while (and items (< number 9))
1818 (setq current (car items)
1819 items (cdr items)
1820 number (1+ number))
1821 (setq entries (cons `[,current
1822 (Info-follow-reference ,current)
1823 t]
1824 entries)))
1825 (if items
1826 (setq entries (cons ["Other..." Info-follow-reference t]
1827 entries)))
1828 (or entries
1829 (setq entries (list ["No references" nil nil])))
1830 (easy-menu-change '("Info") "Reference" (nreverse entries)))
1831 ;; Update last seen node.
6d2c8e3e 1832 (setq Info-menu-last-node (list Info-current-file Info-current-node)))
75a209d4
RS
1833 ;; Try to avoid entering infinite beep mode in case of errors.
1834 (error (ding))))
1835
a384cab3
JB
1836\f
1837;; Info mode is suitable only for specially formatted data.
1838(put 'info-mode 'mode-class 'special)
1839
1840(defun Info-mode ()
1841 "\\<Info-mode-map>
1842Info mode provides commands for browsing through the Info documentation tree.
1843Documentation in Info is divided into \"nodes\", each of which discusses
1844one topic and contains references to other nodes which discuss related
1845topics. Info has commands to follow the references and show you other nodes.
1846
1847\\[Info-help] Invoke the Info tutorial.
6b136ab9 1848\\[Info-exit] Quit Info: reselect previously selected buffer.
a384cab3
JB
1849
1850Selecting other nodes:
21d5959f
RS
1851\\[Info-mouse-follow-nearest-node]
1852 Follow a node reference you click on.
1853 This works with menu items, cross references, and
1854 the \"next\", \"previous\" and \"up\", depending on where you click.
6b136ab9 1855\\[Info-follow-nearest-node] Follow a node reference near point, like \\[Info-mouse-follow-nearest-node].
a384cab3 1856\\[Info-next] Move to the \"next\" node of this node.
bc2ada62 1857\\[Info-prev] Move to the \"previous\" node of this node.
a384cab3
JB
1858\\[Info-up] Move \"up\" from this node.
1859\\[Info-menu] Pick menu item specified by name (or abbreviation).
1860 Picking a menu item causes another node to be selected.
aea2a8da 1861\\[Info-directory] Go to the Info directory node.
a384cab3
JB
1862\\[Info-follow-reference] Follow a cross reference. Reads name of reference.
1863\\[Info-last] Move to the last node you were at.
1143a6b0
ER
1864\\[Info-index] Look up a topic in this file's Index and move to that node.
1865\\[Info-index-next] (comma) Move to the next match from a previous `i' command.
6b136ab9
DL
1866\\[Info-top-node] Go to the Top node of this file.
1867\\[Info-final-node] Go to the final node in this file.
1868\\[Info-backward-node] Go backward one node, considering all nodes as forming one sequence.
1869\\[Info-forward-node] Go forward one node, considering all nodes as forming one sequence.
a384cab3
JB
1870
1871Moving within a node:
177c3549
RS
1872\\[Info-scroll-up] Normally, scroll forward a full screen.
1873Once you scroll far enough in a node that its menu appears on the screen
1874but after point, the next scroll moves into its first subnode.
1875When after all menu items (or if their is no menu), move up to
1876the parent node.
1877\\[Info-scroll-down] Normally, scroll backward. If the beginning of the buffer is
803eaf50
ER
1878already visible, try to go to the previous menu entry, or up if there is none.
1879\\[beginning-of-buffer] Go to beginning of node.
a384cab3 1880
a384cab3
JB
1881Advanced commands:
1882\\[Info-exit] Quit Info: reselect previously selected buffer.
1883\\[Info-edit] Edit contents of selected node.
18841 Pick first item in node's menu.
18852, 3, 4, 5 Pick second ... fifth item in node's menu.
1886\\[Info-goto-node] Move to node specified by name.
1887 You may include a filename as well, as (FILENAME)NODENAME.
552775bd 1888\\[universal-argument] \\[info] Move to new Info file with completion.
a384cab3 1889\\[Info-search] Search through this Info file for specified regexp,
803eaf50 1890 and select the node in which the next occurrence is found.
552775bd
RS
1891\\[Info-next-reference] Move cursor to next cross-reference or menu item.
1892\\[Info-prev-reference] Move cursor to previous cross-reference or menu item."
a384cab3
JB
1893 (kill-all-local-variables)
1894 (setq major-mode 'Info-mode)
1895 (setq mode-name "Info")
f73299f3 1896 (setq tab-width 8)
a384cab3 1897 (use-local-map Info-mode-map)
75a209d4
RS
1898 (make-local-hook 'activate-menubar-hook)
1899 (add-hook 'activate-menubar-hook 'Info-menu-update nil t)
a384cab3
JB
1900 (set-syntax-table text-mode-syntax-table)
1901 (setq local-abbrev-table text-mode-abbrev-table)
1902 (setq case-fold-search t)
1903 (setq buffer-read-only t)
a384cab3
JB
1904 (make-local-variable 'Info-current-file)
1905 (make-local-variable 'Info-current-subfile)
1906 (make-local-variable 'Info-current-node)
1907 (make-local-variable 'Info-tag-table-marker)
c5fe2ff1
RS
1908 (setq Info-tag-table-marker (make-marker))
1909 (make-local-variable 'Info-tag-table-buffer)
1910 (setq Info-tag-table-buffer nil)
a384cab3 1911 (make-local-variable 'Info-history)
1143a6b0 1912 (make-local-variable 'Info-index-alternatives)
93480d70
RS
1913 ;; This is for the sake of the invisible text we use handling titles.
1914 (make-local-variable 'line-move-ignore-invisible)
1915 (setq line-move-ignore-invisible t)
a384cab3
JB
1916 (Info-set-mode-line)
1917 (run-hooks 'Info-mode-hook))
1918
1919(defvar Info-edit-map nil
1920 "Local keymap used within `e' command of Info.")
1921(if Info-edit-map
1922 nil
1923 (setq Info-edit-map (nconc (make-sparse-keymap) text-mode-map))
1924 (define-key Info-edit-map "\C-c\C-c" 'Info-cease-edit))
1925
1926;; Info-edit mode is suitable only for specially formatted data.
1927(put 'info-edit-mode 'mode-class 'special)
1928
1929(defun Info-edit-mode ()
1930 "Major mode for editing the contents of an Info node.
a426b157 1931Like text mode with the addition of `Info-cease-edit'
a384cab3
JB
1932which returns to Info mode for browsing.
1933\\{Info-edit-map}"
a384cab3
JB
1934 (use-local-map Info-edit-map)
1935 (setq major-mode 'Info-edit-mode)
1936 (setq mode-name "Info Edit")
1937 (kill-local-variable 'mode-line-buffer-identification)
1938 (setq buffer-read-only nil)
2c609f53 1939 (force-mode-line-update)
e82c28f9
KH
1940 (buffer-enable-undo (current-buffer))
1941 (run-hooks 'Info-edit-mode-hook))
1942
1943(defun Info-edit ()
1944 "Edit the contents of this Info node.
1945Allowed only if variable `Info-enable-edit' is non-nil."
1946 (interactive)
1947 (or Info-enable-edit
1948 (error "Editing info nodes is not enabled"))
1949 (Info-edit-mode)
8ab3e50b 1950 (message "%s" (substitute-command-keys
e82c28f9 1951 "Editing: Type \\<Info-edit-map>\\[Info-cease-edit] to return to info")))
a384cab3
JB
1952
1953(defun Info-cease-edit ()
1954 "Finish editing Info node; switch back to Info proper."
1955 (interactive)
1956 ;; Do this first, so nothing has changed if user C-g's at query.
1957 (and (buffer-modified-p)
1958 (y-or-n-p "Save the file? ")
1959 (save-buffer))
1960 (use-local-map Info-mode-map)
1961 (setq major-mode 'Info-mode)
1962 (setq mode-name "Info")
1963 (Info-set-mode-line)
1964 (setq buffer-read-only t)
2c609f53 1965 (force-mode-line-update)
a384cab3
JB
1966 (and (marker-position Info-tag-table-marker)
1967 (buffer-modified-p)
1968 (message "Tags may have changed. Use Info-tagify if necessary")))
f0a8a3f1 1969\f
f88ab1c9
RS
1970(defvar Info-file-list-for-emacs
1971 '("ediff" "forms" "gnus" "info" ("mh" . "mh-e") "sc")
1972 "List of Info files that describe Emacs commands.
1973An element can be a file name, or a list of the form (PREFIX . FILE)
1974where PREFIX is a name prefix and FILE is the file to look in.
1975If the element is just a file name, the file name also serves as the prefix.")
1976
f0a8a3f1 1977(defun Info-find-emacs-command-nodes (command)
f88ab1c9 1978 "Return a list of locations documenting COMMAND.
f57b2cd8
RS
1979The `info-file' property of COMMAND says which Info manual to search.
1980If COMMAND has no property, the variable `Info-file-list-for-emacs'
1981defines heuristics for which Info manual to try.
f0a8a3f1
RM
1982The locations are of the format used in Info-history, i.e.
1983\(FILENAME NODENAME BUFFERPOS\)."
f0a8a3f1 1984 (let ((where '())
b37daea4 1985 (cmd-desc (concat "^\\* +" (regexp-quote (symbol-name command))
f88ab1c9
RS
1986 ":\\s *\\(.*\\)\\.$"))
1987 (info-file "emacs")) ;default
1988 ;; Determine which info file this command is documented in.
1989 (if (get command 'info-file)
1990 (setq info-file (get command 'info-file))
1991 ;; If it doesn't say explicitly, test its name against
1992 ;; various prefixes that we know.
1993 (let ((file-list Info-file-list-for-emacs))
1994 (while file-list
1995 (let* ((elt (car file-list))
1996 (name (if (consp elt)
1997 (car elt)
1998 elt))
1999 (file (if (consp elt) (cdr elt) elt))
f57b2cd8 2000 (regexp (concat "\\`" (regexp-quote name)
f88ab1c9
RS
2001 "\\(\\'\\|-\\)")))
2002 (if (string-match regexp (symbol-name command))
2003 (setq info-file file file-list nil))
2004 (setq file-list (cdr file-list))))))
f0a8a3f1 2005 (save-excursion
f88ab1c9
RS
2006 (condition-case nil
2007 (Info-find-node info-file "Command Index")
2008 ;; Some manuals may not have a separate Command Index node,
2009 ;; so try just Index instead.
2010 (error
2011 (Info-find-node info-file "Index")))
f0a8a3f1
RM
2012 ;; Take the index node off the Info history.
2013 (setq Info-history (cdr Info-history))
2014 (goto-char (point-max))
2015 (while (re-search-backward cmd-desc nil t)
2016 (setq where (cons (list Info-current-file
2017 (buffer-substring
2018 (match-beginning 1)
2019 (match-end 1))
2020 0)
2021 where)))
2022 where)))
2023
2024;;;###autoload
2025(defun Info-goto-emacs-command-node (command)
e0568e86 2026 "Go to the Info node in the Emacs manual for command COMMAND.
f88ab1c9 2027The command is found by looking up in Emacs manual's Command Index
f57b2cd8
RS
2028or in another manual found via COMMAND's `info-file' property or
2029the variable `Info-file-list-for-emacs'."
f0a8a3f1
RM
2030 (interactive "CFind documentation for command: ")
2031 (or (commandp command)
2032 (signal 'wrong-type-argument (list 'commandp command)))
2033 (let ((where (Info-find-emacs-command-nodes command)))
2034 (if where
2035 (let ((num-matches (length where)))
2036 ;; Get Info running, and pop to it in another window.
2037 (save-window-excursion
2038 (info))
c5fe2ff1
RS
2039 ;; FIXME It would be cool if this could use a buffer other
2040 ;; than *info*.
f0a8a3f1
RM
2041 (pop-to-buffer "*info*")
2042 (Info-find-node (car (car where))
2043 (car (cdr (car where))))
2044 (if (> num-matches 1)
2045 (progn
2046 ;; Info-find-node already pushed (car where) onto
2047 ;; Info-history. Put the other nodes that were found on
2048 ;; the history.
2049 (setq Info-history (nconc (cdr where) Info-history))
8ab3e50b 2050 (message "Found %d other entr%s. Use %s to see %s."
cedb118c
RS
2051 (1- num-matches)
2052 (if (> num-matches 2) "ies" "y")
8ab3e50b 2053 (substitute-command-keys "\\[Info-last]")
cedb118c 2054 (if (> num-matches 2) "them" "it")))))
e9b81433 2055 (error "Couldn't find documentation for %s" command))))
f0a8a3f1
RM
2056
2057;;;###autoload
2058(defun Info-goto-emacs-key-command-node (key)
2059 "Go to the Info node in the Emacs manual the command bound to KEY, a string.
e0568e86 2060Interactively, if the binding is execute-extended-command, a command is read.
f88ab1c9 2061The command is found by looking up in Emacs manual's Command Index
f57b2cd8
RS
2062or in another manual found via COMMAND's `info-file' property or
2063the variable `Info-file-list-for-emacs'."
f0a8a3f1
RM
2064 (interactive "kFind documentation for key:")
2065 (let ((command (key-binding key)))
2066 (cond ((null command)
9e5c2f50 2067 (message "%s is undefined" (key-description key)))
f0a8a3f1
RM
2068 ((and (interactive-p)
2069 (eq command 'execute-extended-command))
2070 (Info-goto-emacs-command-node
2071 (read-command "Find documentation for command: ")))
2072 (t
2073 (Info-goto-emacs-command-node command)))))
552775bd 2074\f
ded3e3d8 2075(defcustom Info-title-face-alist
98d8273c
KH
2076 '((?* bold underline)
2077 (?= bold-italic underline)
2078 (?- italic underline))
2079 "*Alist of face or list of faces to use for pseudo-underlined titles.
ded3e3d8
RS
2080The alist key is the character the title is underlined with (?*, ?= or ?-)."
2081 :type '(repeat (list character face face))
2082 :group 'info)
98d8273c 2083
552775bd
RS
2084(defun Info-fontify-node ()
2085 (save-excursion
1bcedb3b
RS
2086 (let ((buffer-read-only nil)
2087 (case-fold-search t))
552775bd 2088 (goto-char (point-min))
35d2d241
RS
2089 (when (looking-at "^File: [^,: \t]+,?[ \t]+")
2090 (goto-char (match-end 0))
2091 (while
2092 (looking-at "[ \t]*\\([^:, \t\n]+\\):[ \t]+\\([^:,\t\n]+\\),?")
2093 (goto-char (match-end 0))
2094 (if (save-excursion
2095 (goto-char (match-beginning 1))
2096 (save-match-data (looking-at "Node:")))
2097 (put-text-property (match-beginning 2) (match-end 2)
2098 'face 'info-node)
2099 (put-text-property (match-beginning 2) (match-end 2)
2100 'face 'info-xref)
2101 (put-text-property (match-beginning 2) (match-end 2)
2102 'mouse-face 'highlight))))
552775bd 2103 (goto-char (point-min))
98d8273c
KH
2104 (while (re-search-forward "\n\\([^ \t\n].+\\)\n\\(\\*+\\|=+\\|-+\\)$"
2105 nil t)
2106 (put-text-property (match-beginning 1) (match-end 1)
2107 'face
2e518b7d 2108 (cdr (assq (preceding-char) Info-title-face-alist)))
93480d70
RS
2109 ;; This is a serious problem for trying to handle multiple
2110 ;; frame types at once. We want this text to be invisible
2111 ;; on frames that can display the font above.
2112 (if (memq (framep (selected-frame)) '(x pc w32))
2113 (put-text-property (match-end 1) (match-end 2)
2114 'invisible t)))
98d8273c 2115 (goto-char (point-min))
7757c476 2116 (while (re-search-forward "\\*Note[ \n\t]+\\([^:]*\\):" nil t)
552775bd
RS
2117 (if (= (char-after (1- (match-beginning 0))) ?\") ; hack
2118 nil
2119 (put-text-property (match-beginning 1) (match-end 1)
9a87b430
RS
2120 'face 'info-xref)
2121 (put-text-property (match-beginning 1) (match-end 1)
2122 'mouse-face 'highlight)))
552775bd
RS
2123 (goto-char (point-min))
2124 (if (and (search-forward "\n* Menu:" nil t)
2125 (not (string-match "\\<Index\\>" Info-current-node))
2126 ;; Don't take time to annotate huge menus
34a0a4ee 2127 (< (- (point-max) (point)) Info-fontify-maximum-menu-size))
552775bd 2128 (let ((n 0))
b37daea4 2129 (while (re-search-forward "^\\* +\\([^:\t\n]*\\):" nil t)
552775bd
RS
2130 (setq n (1+ n))
2131 (if (memq n '(5 9)) ; visual aids to help with 1-9 keys
2132 (put-text-property (match-beginning 0)
2133 (1+ (match-beginning 0))
2134 'face 'info-menu-5))
2135 (put-text-property (match-beginning 1) (match-end 1)
35d2d241 2136 'face 'info-xref)
9a87b430
RS
2137 (put-text-property (match-beginning 1) (match-end 1)
2138 'mouse-face 'highlight))))
552775bd 2139 (set-buffer-modified-p nil))))
c5fe2ff1
RS
2140\f
2141
2142;; When an Info buffer is killed, make sure the associated tags buffer
2143;; is killed too.
2144(defun Info-kill-buffer ()
2145 (and (eq major-mode 'Info-mode)
2146 Info-tag-table-buffer
2147 (kill-buffer Info-tag-table-buffer)))
2148
2149(add-hook 'kill-buffer-hook 'Info-kill-buffer)
3cb6768f
EL
2150
2151;;; Speedbar support:
2152;; These functions permit speedbar to display the "tags" in the
2153;; current info node.
96ee3f29 2154(eval-when-compile (require 'speedbar))
3cb6768f 2155
96ee3f29
KH
2156(defvar Info-speedbar-key-map nil
2157 "Keymap used when in the info display mode.")
3cb6768f 2158
96ee3f29
KH
2159(defun Info-install-speedbar-variables ()
2160 "Install those variables used by speedbar to enhance Info."
2161 (if Info-speedbar-key-map
2162 nil
2163 (setq Info-speedbar-key-map (speedbar-make-specialized-keymap))
2164
2165 ;; Basic tree features
2166 (define-key Info-speedbar-key-map "e" 'speedbar-edit-line)
2167 (define-key Info-speedbar-key-map "\C-m" 'speedbar-edit-line)
2168 (define-key Info-speedbar-key-map "+" 'speedbar-expand-line)
2169 (define-key Info-speedbar-key-map "-" 'speedbar-contract-line)
2170 )
2171
2172 (speedbar-add-expansion-list '("Info" Info-speedbar-menu-items
2173 Info-speedbar-key-map
2174 Info-speedbar-hierarchy-buttons)))
3cb6768f
EL
2175
2176(defvar Info-speedbar-menu-items
96ee3f29
KH
2177 '(["Browse Node" speedbar-edit-line t]
2178 ["Expand Node" speedbar-expand-line
2179 (save-excursion (beginning-of-line)
2180 (looking-at "[0-9]+: *.\\+. "))]
2181 ["Contract Node" speedbar-contract-line
2182 (save-excursion (beginning-of-line)
2183 (looking-at "[0-9]+: *.-. "))]
2184 )
3cb6768f
EL
2185 "Additional menu-items to add to speedbar frame.")
2186
96ee3f29
KH
2187;; Make sure our special speedbar major mode is loaded
2188(if (featurep 'speedbar)
2189 (Info-install-speedbar-variables)
2190 (add-hook 'speedbar-load-hook 'Info-install-speedbar-variables))
2191
2192;;; Info hierarchy display method
2193;;;###autoload
2194(defun Info-speedbar-browser ()
2195 "Initialize speedbar to display an info node browser.
2196This will add a speedbar major display mode."
2197 (interactive)
2198 (require 'speedbar)
2199 ;; Make sure that speedbar is active
2200 (speedbar-frame-mode 1)
2201 ;; Now, throw us into Info mode on speedbar.
2202 (speedbar-change-initial-expansion-list "Info")
2203 )
2204
2205(defun Info-speedbar-hierarchy-buttons (directory depth &optional node)
2206 "Display an Info directory hierarchy in speedbar.
2207DIRECTORY is the current directory in the attached frame.
2208DEPTH is the current indentation depth.
2209NODE is an optional argument that is used to represent the
2210specific node to expand."
2211 (if (and (not node)
2212 (save-excursion (goto-char (point-min))
1bcedb3b
RS
2213 (let ((case-fold-search t))
2214 (looking-at "Info Nodes:"))))
96ee3f29
KH
2215 ;; Update our "current node" maybe?
2216 nil
2217 ;; We cannot use the generic list code, that depends on all leaves
2218 ;; being known at creation time.
2219 (if (not node)
2220 (speedbar-with-writable (insert "Info Nodes:\n")))
5fdc7997
EL
2221 (let ((completions nil)
2222 (cf (selected-frame)))
2223 (select-frame speedbar-attached-frame)
2224 (save-window-excursion
2225 (setq completions
2226 (Info-speedbar-fetch-file-nodes (or node '"(dir)top"))))
2227 (select-frame cf)
96ee3f29
KH
2228 (if completions
2229 (speedbar-with-writable
2230 (while completions
2231 (speedbar-make-tag-line 'bracket ?+ 'Info-speedbar-expand-node
2232 (cdr (car completions))
2233 (car (car completions))
2234 'Info-speedbar-goto-node
2235 (cdr (car completions))
2236 'info-xref depth)
2237 (setq completions (cdr completions)))
2238 t)
2239 nil))))
2240
2241(defun Info-speedbar-goto-node (text node indent)
2242 "When user clicks on TEXT, goto an info NODE.
2243The INDENT level is ignored."
2244 (select-frame speedbar-attached-frame)
2245 (let* ((buff (or (get-buffer "*info*")
2246 (progn (info) (get-buffer "*info*"))))
2247 (bwin (get-buffer-window buff 0)))
2248 (if bwin
2249 (progn
2250 (select-window bwin)
2251 (raise-frame (window-frame bwin)))
2252 (if speedbar-power-click
2253 (let ((pop-up-frames t)) (select-window (display-buffer buff)))
2254 (select-frame speedbar-attached-frame)
2255 (switch-to-buffer buff)))
2256 (let ((junk (string-match "^(\\([^)]+\\))\\([^.]+\\)$" node))
2257 (file (match-string 1 node))
2258 (node (match-string 2 node)))
2259 (Info-find-node file node)
2260 ;; If we do a find-node, and we were in info mode, restore
2261 ;; the old default method. Once we are in info mode, it makes
2262 ;; sense to return to whatever method the user was using before.
2263 (if (string= speedbar-initial-expansion-list-name "Info")
2264 (speedbar-change-initial-expansion-list
2265 speedbar-previously-used-expansion-list-name)))))
2266
2267(defun Info-speedbar-expand-node (text token indent)
2268 "Expand the node the user clicked on.
2269TEXT is the text of the button we clicked on, a + or - item.
2270TOKEN is data related to this node (NAME . FILE).
2271INDENT is the current indentation depth."
2272 (cond ((string-match "+" text) ;we have to expand this file
2273 (speedbar-change-expand-button-char ?-)
2274 (if (speedbar-with-writable
2275 (save-excursion
2276 (end-of-line) (forward-char 1)
2277 (Info-speedbar-hierarchy-buttons nil (1+ indent) token)))
2278 (speedbar-change-expand-button-char ?-)
2279 (speedbar-change-expand-button-char ??)))
2280 ((string-match "-" text) ;we have to contract this node
2281 (speedbar-change-expand-button-char ?+)
2282 (speedbar-delete-subblock indent))
2283 (t (error "Ooops... not sure what to do")))
2284 (speedbar-center-buffer-smartly))
2285
2286(defun Info-speedbar-fetch-file-nodes (nodespec)
2287 "Fetch the subnodes from the info NODESPEC.
2288NODESPEC is a string of the form: (file)node.
2289Optional THISFILE represends the filename of"
2290 (save-excursion
2291 ;; Set up a buffer we can use to fake-out Info.
2292 (set-buffer (get-buffer-create "*info-browse-tmp*"))
2293 (if (not (equal major-mode 'Info-mode))
2294 (Info-mode))
2295 ;; Get the node into this buffer
2296 (let ((junk (string-match "^(\\([^)]+\\))\\([^.]+\\)$" nodespec))
2297 (file (match-string 1 nodespec))
2298 (node (match-string 2 nodespec)))
2299 (Info-find-node file node))
2300 ;; Scan the created buffer
2301 (goto-char (point-min))
2302 (let ((completions nil)
1bcedb3b 2303 (case-fold-search t)
96ee3f29
KH
2304 (thisfile (progn (string-match "^(\\([^)]+\\))" nodespec)
2305 (match-string 1 nodespec))))
2306 ;; Always skip the first one...
2307 (re-search-forward "\n\\* \\([^:\t\n]*\\):" nil t)
2308 (while (re-search-forward "\n\\* \\([^:\t\n]*\\):" nil t)
2309 (let ((name (match-string 1)))
2310 (if (looking-at " *\\(([^)]+)[^.\n]+\\)\\.")
2311 (setq name (cons name (match-string 1)))
2312 (if (looking-at " *\\(([^)]+)\\)\\.")
2313 (setq name (cons name (concat (match-string 1) "Top")))
2314 (if (looking-at " \\([^.]+\\).")
2315 (setq name
2316 (cons name (concat "(" thisfile ")" (match-string 1))))
2317 (setq name (cons name (concat "(" thisfile ")" name))))))
2318 (setq completions (cons name completions))))
2319 (nreverse completions))))
2320
2321;;; Info mode node listing
3cb6768f
EL
2322(defun Info-speedbar-buttons (buffer)
2323 "Create a speedbar display to help navigation in an Info file.
2324BUFFER is the buffer speedbar is requesting buttons for."
96ee3f29 2325 (if (save-excursion (goto-char (point-min))
1bcedb3b
RS
2326 (let ((case-fold-search t))
2327 (not (looking-at "Info Nodes:"))))
96ee3f29
KH
2328 (erase-buffer))
2329 (Info-speedbar-hierarchy-buttons nil 0)
2330 )
49116ac0
JB
2331
2332(provide 'info)
2333
1a06eabd 2334;;; info.el ends here