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