*** empty log message ***
[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)
fb163355
MB
775 (while (and (eolp) (not (eobp)))
776 (forward-line 1))
7ea13762 777 (setq beg (point))
ba42ce14
EZ
778 (or (search-backward "\n\^_" nil 'move)
779 (looking-at "\^_")
780 (signal 'search-failed (list "\n\^_")))
7ea13762
RS
781 (search-forward "Node: ")
782 (setq nodename (Info-following-node-name))
3a6ade8a 783 (search-forward "\n\^_" nil 'move)
7ea13762
RS
784 (beginning-of-line)
785 (setq end (point))
4fba3b2c
RS
786 (setq this-buffer-nodes
787 (cons (list nodename other beg end)
788 this-buffer-nodes))))
789 (if (assoc-ignore-case "top" this-buffer-nodes)
790 (setq nodes (nconc this-buffer-nodes nodes))
791 (setq problems t)
792 (message "No `top' node in %s" Info-dir-file-name))))
7ea13762 793 (setq others (cdr others)))
fdf4b680
DL
794 ;; Add to the main menu a menu item for each other node.
795 (let ((case-fold-search t)
760d5cb3 796 (re-search-forward "^\\* Menu:")))
7ea13762
RS
797 (forward-line 1)
798 (let ((menu-items '("top"))
799 (nodes nodes)
800 (case-fold-search t)
3a6ade8a 801 (end (save-excursion (search-forward "\^_" nil t) (point))))
7ea13762
RS
802 (while nodes
803 (let ((nodename (car (car nodes))))
c3a29d70
RS
804 (save-excursion
805 (or (member (downcase nodename) menu-items)
b37daea4 806 (re-search-forward (concat "^\\* +"
c3a29d70
RS
807 (regexp-quote nodename)
808 "::")
809 end t)
810 (progn
811 (insert "* " nodename "::" "\n")
812 (setq menu-items (cons nodename menu-items))))))
7ea13762
RS
813 (setq nodes (cdr nodes))))
814 ;; Now take each node of each of the other buffers
815 ;; and merge it into the main buffer.
816 (while nodes
760d5cb3
GM
817 (let ((case-fold-search t)
818 (nodename (car (car nodes))))
7ea13762
RS
819 (goto-char (point-min))
820 ;; Find the like-named node in the main buffer.
ba42ce14 821 (if (re-search-forward (concat "^\^_.*\n.*Node: "
7ea13762
RS
822 (regexp-quote nodename)
823 "[,\n\t]")
824 nil t)
825 (progn
3a6ade8a 826 (search-forward "\n\^_" nil 'move)
25869acf
RS
827 (beginning-of-line)
828 (insert "\n"))
7ea13762
RS
829 ;; If none exists, add one.
830 (goto-char (point-max))
dfbf6104 831 (insert "\^_\nFile: dir\tNode: " nodename "\n\n* Menu:\n\n"))
7ea13762
RS
832 ;; Merge the text from the other buffer's menu
833 ;; into the menu in the like-named node in the main buffer.
25869acf 834 (apply 'insert-buffer-substring (cdr (car nodes))))
7ea13762
RS
835 (setq nodes (cdr nodes)))
836 ;; Kill all the buffers we just made.
837 (while buffers
838 (kill-buffer (car buffers))
825d6f08 839 (setq buffers (cdr buffers)))
973a3104 840 (if Info-fontify (Info-fontify-menu-headers))
4fba3b2c
RS
841 (goto-char (point-min))
842 (if problems
843 (message "Composing main Info directory...problems encountered, see `*Messages*'")
844 (message "Composing main Info directory...done")))
44c327f9
JB
845 (setq Info-dir-contents (buffer-string)))
846 (setq default-directory Info-dir-contents-directory))
7ea13762 847
c5fe2ff1
RS
848;; Note that on entry to this function the current-buffer must be the
849;; *info* buffer; not the info tags buffer.
a384cab3 850(defun Info-read-subfile (nodepos)
a361deba
RS
851 ;; NODEPOS is either a position (in the Info file as a whole,
852 ;; not relative to a subfile) or the name of a subfile.
a384cab3
JB
853 (let (lastfilepos
854 lastfilename)
a361deba
RS
855 (if (numberp nodepos)
856 (save-excursion
857 (set-buffer (marker-buffer Info-tag-table-marker))
858 (goto-char (point-min))
ba42ce14
EZ
859 (or (looking-at "\^_")
860 (search-forward "\n\^_"))
a361deba
RS
861 (forward-line 2)
862 (catch 'foo
863 (while (not (looking-at "\^_"))
864 (if (not (eolp))
865 (let ((beg (point))
866 thisfilepos thisfilename)
867 (search-forward ": ")
868 (setq thisfilename (buffer-substring beg (- (point) 2)))
869 (setq thisfilepos (read (current-buffer)))
870 ;; read in version 19 stops at the end of number.
871 ;; Advance to the next line.
872 (forward-line 1)
873 (if (> thisfilepos nodepos)
874 (throw 'foo t))
875 (setq lastfilename thisfilename)
876 (setq lastfilepos thisfilepos))
877 (forward-line 1)))))
878 (setq lastfilename nodepos)
879 (setq lastfilepos 0))
c5fe2ff1
RS
880 ;; Assume previous buffer is in Info-mode.
881 ;; (set-buffer (get-buffer "*info*"))
a384cab3
JB
882 (or (equal Info-current-subfile lastfilename)
883 (let ((buffer-read-only nil))
884 (setq buffer-file-name nil)
885 (widen)
886 (erase-buffer)
1143a6b0 887 (info-insert-file-contents lastfilename)
a384cab3
JB
888 (set-buffer-modified-p nil)
889 (setq Info-current-subfile lastfilename)))
890 (goto-char (point-min))
ba42ce14
EZ
891 (if (looking-at "\^_")
892 (forward-char 1)
893 (search-forward "\n\^_"))
a361deba
RS
894 (if (numberp nodepos)
895 (+ (- nodepos lastfilepos) (point)))))
a384cab3 896
50ac70af
MB
897(defvar Info-header-line nil
898 "If the info node header is hidden, the text of the header.")
899
a384cab3 900(defun Info-select-node ()
fdf4b680
DL
901"Select the info node that point is in.
902Bind this in case the user sets it to nil."
1bcedb3b
RS
903 (let ((case-fold-search t))
904 (save-excursion
760d5cb3
GM
905 ;; Find beginning of node.
906 (if (search-backward "\n\^_" nil 'move)
907 (forward-line 2)
908 (if (looking-at "\^_")
909 (forward-line 1)
910 (signal 'search-failed (list "\n\^_"))))
911 ;; Get nodename spelled as it is in the node.
912 (re-search-forward "Node:[ \t]*")
913 (setq Info-current-node
914 (buffer-substring-no-properties (point)
915 (progn
916 (skip-chars-forward "^,\t\n")
917 (point))))
918 (Info-set-mode-line)
919 ;; Find the end of it, and narrow.
920 (beginning-of-line)
921 (let (active-expression)
50ac70af 922 ;; Narrow to the node contents
760d5cb3
GM
923 (narrow-to-region (point)
924 (if (re-search-forward "\n[\^_\f]" nil t)
925 (prog1
926 (1- (point))
927 (if (looking-at "[\n\^_\f]*execute: ")
928 (progn
929 (goto-char (match-end 0))
930 (setq active-expression
931 (read (current-buffer))))))
932 (point-max)))
933 (if Info-enable-active-nodes (eval active-expression))
934 (if Info-fontify (Info-fontify-node))
50ac70af
MB
935 (if Info-use-header-line
936 (Info-setup-header-line)
937 (setq Info-header-line nil))
760d5cb3 938 (run-hooks 'Info-selection-hook)))))
a384cab3
JB
939
940(defun Info-set-mode-line ()
941 (setq mode-line-buffer-identification
313fe58a 942 (nconc (propertized-buffer-identification "%b")
918e364a
MB
943 (list
944 (concat " ("
945 (file-name-nondirectory
946 (if (stringp Info-current-file)
947 Info-current-file
948 (or buffer-file-name "")))
949 ") "
950 (or Info-current-node ""))))))
a384cab3 951\f
50ac70af
MB
952;; Skip the node header and make it into a header-line. This function
953;; should be called when the node is already narrowed.
954(defun Info-setup-header-line ()
955 (goto-char (point-min))
956 (forward-line 1)
957 (set (make-local-variable 'Info-header-line)
958 (buffer-substring (point-min) (1- (point))))
959 (setq header-line-format 'Info-header-line)
960 (narrow-to-region (point) (point-max)))
961\f
a384cab3
JB
962;; Go to an info node specified with a filename-and-nodename string
963;; of the sort that is found in pointers in nodes.
964
4fceda3c
SM
965(defun Info-goto-node (nodename &optional fork)
966 "Go to info node named NAME. Give just NODENAME or (FILENAME)NODENAME.
7210b6f5
DL
967If FORK is non-nil (interactively with a prefix arg), show the node in
968a new info buffer.
4fceda3c 969If FORK is a string, it is the name to use for the new buffer."
d1268e52 970 (interactive (list (Info-read-node-name "Go to node: ") current-prefix-arg))
d0e41cba 971 (info-initialize)
4fceda3c 972 (if fork
760d5cb3
GM
973 (set-buffer
974 (clone-buffer (concat "*info-" (if (stringp fork) fork nodename) "*") t)))
a384cab3
JB
975 (let (filename)
976 (string-match "\\s *\\((\\s *\\([^\t)]*\\)\\s *)\\s *\\|\\)\\(.*\\)"
977 nodename)
978 (setq filename (if (= (match-beginning 1) (match-end 1))
979 ""
980 (substring nodename (match-beginning 2) (match-end 2)))
981 nodename (substring nodename (match-beginning 3) (match-end 3)))
982 (let ((trim (string-match "\\s *\\'" filename)))
983 (if trim (setq filename (substring filename 0 trim))))
984 (let ((trim (string-match "\\s *\\'" nodename)))
985 (if trim (setq nodename (substring nodename 0 trim))))
e579232c 986 (if transient-mark-mode (deactivate-mark))
a384cab3
JB
987 (Info-find-node (if (equal filename "") nil filename)
988 (if (equal nodename "") "Top" nodename))))
552775bd 989
ddf89211
RS
990(defvar Info-read-node-completion-table)
991
03524be6 992;; This function is used as the "completion table" while reading a node name.
ddf89211 993;; It does completion using the alist in Info-read-node-completion-table
03524be6
RS
994;; unless STRING starts with an open-paren.
995(defun Info-read-node-name-1 (string predicate code)
996 (let ((no-completion (and (> (length string) 0) (eq (aref string 0) ?\())))
997 (cond ((eq code nil)
998 (if no-completion
999 string
ddf89211 1000 (try-completion string Info-read-node-completion-table predicate)))
03524be6
RS
1001 ((eq code t)
1002 (if no-completion
1003 nil
ddf89211 1004 (all-completions string Info-read-node-completion-table predicate)))
03524be6
RS
1005 ((eq code 'lambda)
1006 (if no-completion
1007 t
ddf89211 1008 (assoc string Info-read-node-completion-table))))))
03524be6 1009
552775bd
RS
1010(defun Info-read-node-name (prompt &optional default)
1011 (let* ((completion-ignore-case t)
ddf89211 1012 (Info-read-node-completion-table (Info-build-node-completions))
b6d61ffa 1013 (nodename (completing-read prompt 'Info-read-node-name-1 nil t)))
552775bd
RS
1014 (if (equal nodename "")
1015 (or default
1016 (Info-read-node-name prompt))
1017 nodename)))
1018
1019(defun Info-build-node-completions ()
1020 (or Info-current-file-completions
1bcedb3b
RS
1021 (let ((compl nil)
1022 ;; Bind this in case the user sets it to nil.
11638562
EZ
1023 (case-fold-search t)
1024 (node-regexp "Node: *\\([^,\n]*\\) *[,\n\t]"))
552775bd
RS
1025 (save-excursion
1026 (save-restriction
1027 (if (marker-buffer Info-tag-table-marker)
c5fe2ff1
RS
1028 (let ((marker Info-tag-table-marker))
1029 (set-buffer (marker-buffer marker))
cedb118c 1030 (widen)
c5fe2ff1 1031 (goto-char marker)
fdf4b680 1032 (while (re-search-forward "\n\\(Node\\|Ref\\): \\(.*\\)\177" nil t)
552775bd 1033 (setq compl
fdf4b680 1034 (cons (list (match-string-no-properties 2))
552775bd
RS
1035 compl))))
1036 (widen)
1037 (goto-char (point-min))
11638562
EZ
1038 ;; If the buffer begins with a node header, process that first.
1039 (if (Info-node-at-bob-matching node-regexp)
110d4e80 1040 (setq compl (list (match-string-no-properties 1))))
11638562 1041 ;; Now for the rest of the nodes.
552775bd
RS
1042 (while (search-forward "\n\^_" nil t)
1043 (forward-line 1)
1044 (let ((beg (point)))
1045 (forward-line 1)
11638562 1046 (if (re-search-backward node-regexp beg t)
399c88ad 1047 (setq compl
110d4e80 1048 (cons (list (match-string-no-properties 1))
552775bd 1049 compl))))))))
87d2b5a2 1050 (setq compl (cons '("*") compl))
552775bd 1051 (setq Info-current-file-completions compl))))
a384cab3 1052\f
aea2a8da
JB
1053(defun Info-restore-point (hl)
1054 "If this node has been visited, restore the point value when we left."
cedb118c
RS
1055 (while hl
1056 (if (and (equal (nth 0 (car hl)) Info-current-file)
e90d1271
RS
1057 ;; Use string-equal, not equal, to ignore text props.
1058 (string-equal (nth 1 (car hl)) Info-current-node))
cedb118c 1059 (progn
d96504df
KH
1060 (goto-char (nth 2 (car hl)))
1061 (setq hl nil)) ;terminate the while at next iter
cedb118c 1062 (setq hl (cdr hl)))))
aea2a8da 1063\f
2c80a618
GM
1064(defvar Info-search-history nil
1065 "The history list for `Info-search'.")
a384cab3
JB
1066
1067(defun Info-search (regexp)
1068 "Search for REGEXP, starting from point, and select node it's found in."
2c80a618
GM
1069 (interactive (list (read-string "Regexp search: "
1070 nil 'Info-search-history)))
1071 (when transient-mark-mode
1072 (deactivate-mark))
1073 (when (equal regexp "")
1074 (setq regexp (car Info-search-history)))
c47b5bbe
DL
1075 (when regexp
1076 (let ((found ()) current
1077 (onode Info-current-node)
1078 (ofile Info-current-file)
1079 (opoint (point))
1080 (ostart (window-start))
1081 (osubfile Info-current-subfile))
1082 (save-excursion
1083 (save-restriction
1084 (widen)
1085 (if (null Info-current-subfile)
a384cab3 1086 (progn (re-search-forward regexp) (setq found (point)))
c47b5bbe
DL
1087 (condition-case err
1088 (progn (re-search-forward regexp) (setq found (point)))
1089 (search-failed nil)))))
760d5cb3 1090 (if (not found) ;can only happen in subfile case -- else would have erred
c47b5bbe
DL
1091 (unwind-protect
1092 (let ((list ()))
1093 (save-excursion
1094 (set-buffer (marker-buffer Info-tag-table-marker))
c5fe2ff1 1095 (goto-char (point-min))
c47b5bbe
DL
1096 (search-forward "\n\^_\nIndirect:")
1097 (save-restriction
1098 (narrow-to-region (point)
1099 (progn (search-forward "\n\^_")
1100 (1- (point))))
1101 (goto-char (point-min))
1102 (search-forward (concat "\n" osubfile ": "))
1103 (beginning-of-line)
1104 (while (not (eobp))
1105 (re-search-forward "\\(^.*\\): [0-9]+$")
1106 (goto-char (+ (match-end 1) 2))
1107 (setq list (cons (cons (read (current-buffer))
110d4e80 1108 (match-string-no-properties 1))
c47b5bbe
DL
1109 list))
1110 (goto-char (1+ (match-end 0))))
1111 (setq list (nreverse list)
1112 current (car (car list))
1113 list (cdr list))))
1114 (while list
1115 (message "Searching subfile %s..." (cdr (car list)))
1116 (Info-read-subfile (car (car list)))
1117 (setq list (cdr list))
1118;;; (goto-char (point-min))
1119 (if (re-search-forward regexp nil t)
1120 (setq found (point) list ())))
1121 (if found
1122 (message "")
1123 (signal 'search-failed (list regexp))))
1124 (if (not found)
1125 (progn (Info-read-subfile osubfile)
1126 (goto-char opoint)
1127 (Info-select-node)
1128 (set-window-start (selected-window) ostart)))))
760d5cb3
GM
1129 (widen)
1130 (goto-char found)
1131 (Info-select-node)
1132 ;; Use string-equal, not equal, to ignore text props.
1133 (or (and (string-equal onode Info-current-node)
1134 (equal ofile Info-current-file))
1135 (setq Info-history (cons (list ofile onode opoint)
1136 Info-history))))))
a384cab3 1137\f
a384cab3 1138(defun Info-extract-pointer (name &optional errorname)
fdf4b680
DL
1139 "Extract the value of the node-pointer named NAME.
1140If there is none, use ERRORNAME in the error message;
1141if ERRORNAME is nil, just return nil.
1142Bind this in case the user sets it to nil."
1bcedb3b
RS
1143 (let ((case-fold-search t))
1144 (save-excursion
50ac70af
MB
1145 (save-restriction
1146 (goto-char (point-min))
1147 (when Info-header-line
1148 ;; expose the header line in the buffer
c1699282
SM
1149 (widen)
1150 (forward-line -1))
50ac70af
MB
1151 (let ((bound (point)))
1152 (forward-line 1)
c1699282 1153 (cond ((re-search-backward (concat name ":") bound t)
0cb39153
MB
1154 (goto-char (match-end 0))
1155 (Info-following-node-name))
50ac70af
MB
1156 ((not (eq errorname t))
1157 (error "Node has no %s"
1158 (capitalize (or errorname name))))))))))
a384cab3
JB
1159
1160(defun Info-following-node-name (&optional allowedchars)
fdf4b680
DL
1161 "Return the node name in the buffer following point.
1162ALLOWEDCHARS, if non-nil, goes within [...] to make a regexp
1163saying which chars may appear in the node name."
a384cab3 1164 (skip-chars-forward " \t")
70d78eb6 1165 (buffer-substring-no-properties
a384cab3
JB
1166 (point)
1167 (progn
1168 (while (looking-at (concat "[" (or allowedchars "^,\t\n") "]"))
1169 (skip-chars-forward (concat (or allowedchars "^,\t\n") "("))
1170 (if (looking-at "(")
1171 (skip-chars-forward "^)")))
1172 (skip-chars-backward " ")
1173 (point))))
1174
1175(defun Info-next ()
1176 "Go to the next node of this node."
1177 (interactive)
1178 (Info-goto-node (Info-extract-pointer "next")))
1179
1180(defun Info-prev ()
1181 "Go to the previous node of this node."
1182 (interactive)
1183 (Info-goto-node (Info-extract-pointer "prev[ious]*" "previous")))
1184
5dab2fb4
RS
1185(defun Info-up (&optional same-file)
1186 "Go to the superior node of this node.
1187If SAME-FILE is non-nil, do not move to a different Info file."
a384cab3 1188 (interactive)
5dab2fb4 1189 (let ((node (Info-extract-pointer "up")))
4fceda3c 1190 (and (or same-file (not (stringp Info-current-file)))
5dab2fb4
RS
1191 (string-match "^(" node)
1192 (error "Up node is in another Info file"))
1193 (Info-goto-node node))
aea2a8da 1194 (Info-restore-point Info-history))
a384cab3
JB
1195
1196(defun Info-last ()
1197 "Go back to the last node visited."
1198 (interactive)
1199 (or Info-history
1200 (error "This is the first Info node you looked at"))
1201 (let (filename nodename opoint)
1202 (setq filename (car (car Info-history)))
1203 (setq nodename (car (cdr (car Info-history))))
1204 (setq opoint (car (cdr (cdr (car Info-history)))))
1205 (setq Info-history (cdr Info-history))
1206 (Info-find-node filename nodename)
1207 (setq Info-history (cdr Info-history))
1208 (goto-char opoint)))
1209
48e5b471 1210;;;###autoload
a384cab3
JB
1211(defun Info-directory ()
1212 "Go to the Info directory node."
1213 (interactive)
1214 (Info-find-node "dir" "top"))
1215\f
1216(defun Info-follow-reference (footnotename)
fdf4b680
DL
1217 "Follow cross reference named FOOTNOTENAME to the node it refers to.
1218FOOTNOTENAME may be an abbreviation of the reference name."
a384cab3
JB
1219 (interactive
1220 (let ((completion-ignore-case t)
1bcedb3b 1221 (case-fold-search t)
67bc89ab 1222 completions default alt-default (start-point (point)) str i bol eol)
a384cab3 1223 (save-excursion
67bc89ab
RS
1224 ;; Store end and beginning of line.
1225 (end-of-line)
1226 (setq eol (point))
1227 (beginning-of-line)
1228 (setq bol (point))
1229
a384cab3
JB
1230 (goto-char (point-min))
1231 (while (re-search-forward "\\*note[ \n\t]*\\([^:]*\\):" nil t)
110d4e80 1232 (setq str (buffer-substring-no-properties
a384cab3
JB
1233 (match-beginning 1)
1234 (1- (point))))
1235 ;; See if this one should be the default.
1236 (and (null default)
1f179e27 1237 (<= (match-beginning 0) start-point)
a384cab3
JB
1238 (<= start-point (point))
1239 (setq default t))
67bc89ab
RS
1240 ;; See if this one should be the alternate default.
1241 (and (null alt-default)
1242 (and (<= bol (match-beginning 0))
1243 (<= (point) eol))
1244 (setq alt-default t))
a384cab3
JB
1245 (setq i 0)
1246 (while (setq i (string-match "[ \n\t]+" str i))
1247 (setq str (concat (substring str 0 i) " "
1248 (substring str (match-end 0))))
1249 (setq i (1+ i)))
1250 ;; Record as a completion and perhaps as default.
1251 (if (eq default t) (setq default str))
67bc89ab 1252 (if (eq alt-default t) (setq alt-default str))
ec6d29af
KH
1253 ;; Don't add this string if it's a duplicate.
1254 ;; We use a loop instead of "(assoc str completions)" because
1255 ;; we want to do a case-insensitive compare.
1256 (let ((tail completions)
1257 (tem (downcase str)))
1258 (while (and tail
1259 (not (string-equal tem (downcase (car (car tail))))))
1260 (setq tail (cdr tail)))
1261 (or tail
1262 (setq completions
1263 (cons (cons str nil)
1264 completions))))))
67bc89ab
RS
1265 ;; If no good default was found, try an alternate.
1266 (or default
1267 (setq default alt-default))
1268 ;; If only one cross-reference found, then make it default.
1269 (if (eq (length completions) 1)
1270 (setq default (car (car completions))))
a384cab3 1271 (if completions
b0ebdfe5
RS
1272 (let ((input (completing-read (if default
1273 (concat "Follow reference named: ("
1274 default ") ")
1275 "Follow reference named: ")
1276 completions nil t)))
1277 (list (if (equal input "")
1278 default input)))
a384cab3 1279 (error "No cross-references in this node"))))
ebf8f7e1
RS
1280
1281 (unless footnotename
1282 (error "No reference was specified"))
1283
1bcedb3b
RS
1284 (let (target beg i (str (concat "\\*note " (regexp-quote footnotename)))
1285 (case-fold-search t))
a384cab3
JB
1286 (while (setq i (string-match " " str i))
1287 (setq str (concat (substring str 0 i) "[ \t\n]+" (substring str (1+ i))))
1288 (setq i (+ i 6)))
1289 (save-excursion
1290 (goto-char (point-min))
1291 (or (re-search-forward str nil t)
1292 (error "No cross-reference named %s" footnotename))
1293 (goto-char (+ (match-beginning 0) 5))
1294 (setq target
1295 (Info-extract-menu-node-name "Bad format cross reference" t)))
1296 (while (setq i (string-match "[ \t\n]+" target i))
1297 (setq target (concat (substring target 0 i) " "
1298 (substring target (match-end 0))))
1299 (setq i (+ i 1)))
1300 (Info-goto-node target)))
1301
1302(defun Info-extract-menu-node-name (&optional errmessage multi-line)
1303 (skip-chars-forward " \t\n")
1304 (let ((beg (point))
1305 str i)
1306 (skip-chars-forward "^:")
1307 (forward-char 1)
1308 (setq str
1309 (if (looking-at ":")
70d78eb6 1310 (buffer-substring-no-properties beg (1- (point)))
a384cab3
JB
1311 (skip-chars-forward " \t\n")
1312 (Info-following-node-name (if multi-line "^.,\t" "^.,\t\n"))))
1313 (while (setq i (string-match "\n" str i))
1314 (aset str i ?\ ))
2e52ff59
RS
1315 ;; Collapse multiple spaces.
1316 (while (string-match " +" str)
1317 (setq str (replace-match " " t t str)))
a384cab3
JB
1318 str))
1319
e8adde3f 1320;; No one calls this.
9e5c2f50
RS
1321;;(defun Info-menu-item-sequence (list)
1322;; (while list
e8adde3f 1323;; (Info-menu (car list))
9e5c2f50 1324;; (setq list (cdr list))))
a384cab3 1325
6356e646
RS
1326(defvar Info-complete-menu-buffer)
1327
e8adde3f 1328(defun Info-complete-menu-item (string predicate action)
760d5cb3
GM
1329 (let ((completion-ignore-case t)
1330 (case-fold-search t))
e8adde3f
RS
1331 (cond ((eq action nil)
1332 (let (completions
b37daea4 1333 (pattern (concat "\n\\* +\\("
e8adde3f
RS
1334 (regexp-quote string)
1335 "[^:\t\n]*\\):")))
1336 (save-excursion
1337 (set-buffer Info-complete-menu-buffer)
1338 (goto-char (point-min))
763da090 1339 (search-forward "\n* Menu:")
e8adde3f 1340 (while (re-search-forward pattern nil t)
110d4e80
DL
1341 (setq completions
1342 (cons (cons (match-string-no-properties 1)
1343 (match-beginning 1))
1344 completions))))
e8adde3f
RS
1345 (try-completion string completions predicate)))
1346 ((eq action t)
1347 (let (completions
b37daea4 1348 (pattern (concat "\n\\* +\\("
e8adde3f
RS
1349 (regexp-quote string)
1350 "[^:\t\n]*\\):")))
1351 (save-excursion
1352 (set-buffer Info-complete-menu-buffer)
1353 (goto-char (point-min))
763da090 1354 (search-forward "\n* Menu:")
e8adde3f 1355 (while (re-search-forward pattern nil t)
110d4e80
DL
1356 (setq completions (cons (cons
1357 (match-string-no-properties 1)
1358 (match-beginning 1))
e8adde3f
RS
1359 completions))))
1360 (all-completions string completions predicate)))
1361 (t
1362 (save-excursion
1363 (set-buffer Info-complete-menu-buffer)
1364 (goto-char (point-min))
763da090 1365 (search-forward "\n* Menu:")
b37daea4 1366 (re-search-forward (concat "\n\\* +"
e8adde3f
RS
1367 (regexp-quote string)
1368 ":")
1369 nil t))))))
1370
1371
4fceda3c 1372(defun Info-menu (menu-item &optional fork)
a384cab3 1373 "Go to node for menu item named (or abbreviated) NAME.
7210b6f5
DL
1374Completion is allowed, and the menu item point is on is the default.
1375If FORK is non-nil (interactively with a prefix arg), show the node in
1376a new info buffer. If FORK is a string, it is the name to use for the
1377new buffer."
a384cab3
JB
1378 (interactive
1379 (let ((completions '())
1380 ;; If point is within a menu item, use that item as the default
1381 (default nil)
1382 (p (point))
211d6309 1383 beg
760d5cb3
GM
1384 (last nil)
1385 (case-fold-search t))
a384cab3
JB
1386 (save-excursion
1387 (goto-char (point-min))
1388 (if (not (search-forward "\n* menu:" nil t))
1389 (error "No menu in this node"))
e8adde3f
RS
1390 (setq beg (point))
1391 (and (< (point) p)
1392 (save-excursion
1393 (goto-char p)
1394 (end-of-line)
4fc14e5a 1395 (if (re-search-backward "\n\\* +\\([^:\t\n]*\\):" beg t)
110d4e80 1396 (setq default (match-string-no-properties 1))))))
a384cab3
JB
1397 (let ((item nil))
1398 (while (null item)
e8adde3f
RS
1399 (setq item (let ((completion-ignore-case t)
1400 (Info-complete-menu-buffer (current-buffer)))
a384cab3
JB
1401 (completing-read (if default
1402 (format "Menu item (default %s): "
1403 default)
760d5cb3 1404 "Menu item: ")
e8adde3f 1405 'Info-complete-menu-item nil t)))
aea2a8da
JB
1406 ;; we rely on the fact that completing-read accepts an input
1407 ;; of "" even when the require-match argument is true and ""
1408 ;; is not a valid possibility
a384cab3
JB
1409 (if (string= item "")
1410 (if default
1411 (setq item default)
760d5cb3
GM
1412 ;; ask again
1413 (setq item nil))))
4fceda3c 1414 (list item current-prefix-arg))))
a384cab3
JB
1415 ;; there is a problem here in that if several menu items have the same
1416 ;; name you can only go to the node of the first with this command.
4fceda3c 1417 (Info-goto-node (Info-extract-menu-item menu-item) (if fork menu-item)))
399c88ad 1418
a384cab3
JB
1419(defun Info-extract-menu-item (menu-item)
1420 (setq menu-item (regexp-quote menu-item))
1bcedb3b
RS
1421 (let ((case-fold-search t))
1422 (save-excursion
760d5cb3
GM
1423 (let ((case-fold-search t))
1424 (goto-char (point-min))
1425 (or (search-forward "\n* menu:" nil t)
1426 (error "No menu in this node"))
1427 (or (re-search-forward (concat "\n\\* +" menu-item ":") nil t)
1428 (re-search-forward (concat "\n\\* +" menu-item) nil t)
1429 (error "No such item in menu"))
1430 (beginning-of-line)
1431 (forward-char 2)
1432 (Info-extract-menu-node-name)))))
a384cab3
JB
1433
1434;; If COUNT is nil, use the last item in the menu.
1435(defun Info-extract-menu-counting (count)
1bcedb3b
RS
1436 (let ((case-fold-search t))
1437 (save-excursion
760d5cb3
GM
1438 (let ((case-fold-search t))
1439 (goto-char (point-min))
1440 (or (search-forward "\n* menu:" nil t)
1441 (error "No menu in this node"))
1442 (if count
1443 (or (search-forward "\n* " nil t count)
1444 (error "Too few items in menu"))
1445 (while (search-forward "\n* " nil t)
1446 nil))
1447 (Info-extract-menu-node-name)))))
a384cab3 1448
e38e7367
RM
1449(defun Info-nth-menu-item ()
1450 "Go to the node of the Nth menu item.
1451N is the digit argument used to invoke this command."
a384cab3 1452 (interactive)
e38e7367
RM
1453 (Info-goto-node
1454 (Info-extract-menu-counting
1455 (- (aref (this-command-keys) (1- (length (this-command-keys)))) ?0))))
a384cab3
JB
1456
1457(defun Info-top-node ()
1458 "Go to the Top node of this file."
1459 (interactive)
1460 (Info-goto-node "Top"))
1461
1462(defun Info-final-node ()
1463 "Go to the final node in this file."
1464 (interactive)
1465 (Info-goto-node "Top")
760d5cb3
GM
1466 (let ((Info-history nil)
1467 (case-fold-search t))
a384cab3
JB
1468 ;; Go to the last node in the menu of Top.
1469 (Info-goto-node (Info-extract-menu-counting nil))
1470 ;; If the last node in the menu is not last in pointer structure,
399c88ad 1471 ;; move forward until we can't go any farther.
a384cab3
JB
1472 (while (Info-forward-node t t) nil)
1473 ;; Then keep moving down to last subnode, unless we reach an index.
1474 (while (and (not (string-match "\\<index\\>" Info-current-node))
1475 (save-excursion (search-forward "\n* Menu:" nil t)))
1476 (Info-goto-node (Info-extract-menu-counting nil)))))
1477
1478(defun Info-forward-node (&optional not-down no-error)
1479 "Go forward one node, considering all nodes as forming one sequence."
1480 (interactive)
1481 (goto-char (point-min))
1482 (forward-line 1)
760d5cb3
GM
1483 (let ((case-fold-search t))
1484 ;; three possibilities, in order of priority:
1485 ;; 1. next node is in a menu in this node (but not in an index)
1486 ;; 2. next node is next at same level
1487 ;; 3. next node is up and next
1488 (cond ((and (not not-down)
1489 (save-excursion (search-forward "\n* menu:" nil t))
1490 (not (string-match "\\<index\\>" Info-current-node)))
1491 (Info-goto-node (Info-extract-menu-counting 1))
1492 t)
1493 ((save-excursion (search-backward "next:" nil t))
1494 (Info-next)
1495 t)
1496 ((and (save-excursion (search-backward "up:" nil t))
1497 ;; Use string-equal, not equal, to ignore text props.
1498 (not (string-equal (downcase (Info-extract-pointer "up"))
1499 "top")))
1500 (let ((old-node Info-current-node))
1501 (Info-up)
1502 (let (Info-history success)
1503 (unwind-protect
1504 (setq success (Info-forward-node t no-error))
1505 (or success (Info-goto-node old-node))))))
1506 (no-error nil)
1507 (t (error "No pointer forward from this node")))))
a384cab3
JB
1508
1509(defun Info-backward-node ()
1510 "Go backward one node, considering all nodes as forming one sequence."
1511 (interactive)
1512 (let ((prevnode (Info-extract-pointer "prev[ious]*" t))
760d5cb3
GM
1513 (upnode (Info-extract-pointer "up" t))
1514 (case-fold-search t))
a384cab3
JB
1515 (cond ((and upnode (string-match "(" upnode))
1516 (error "First node in file"))
1517 ((and upnode (or (null prevnode)
e90d1271
RS
1518 ;; Use string-equal, not equal,
1519 ;; to ignore text properties.
1520 (string-equal (downcase prevnode)
1521 (downcase upnode))))
a384cab3
JB
1522 (Info-up))
1523 (prevnode
1524 ;; If we move back at the same level,
1525 ;; go down to find the last subnode*.
1526 (Info-prev)
1527 (let (Info-history)
1528 (while (and (not (string-match "\\<index\\>" Info-current-node))
1529 (save-excursion (search-forward "\n* Menu:" nil t)))
1530 (Info-goto-node (Info-extract-menu-counting nil)))))
1531 (t
1532 (error "No pointer backward from this node")))))
1533
1534(defun Info-exit ()
1535 "Exit Info by selecting some other buffer."
1536 (interactive)
552775bd
RS
1537 (if Info-standalone
1538 (save-buffers-kill-emacs)
4643e92f 1539 (quit-window)))
a384cab3 1540
253db917
ER
1541(defun Info-next-menu-item ()
1542 (interactive)
fdf4b680 1543 ;; Bind this in case the user sets it to nil.
760d5cb3
GM
1544 (let* ((case-fold-search t)
1545 (node
1546 (save-excursion
1547 (forward-line -1)
1548 (search-forward "\n* menu:" nil t)
1549 (and (search-forward "\n* " nil t)
1550 (Info-extract-menu-node-name)))))
fadfb77f
RS
1551 (if node (Info-goto-node node)
1552 (error "No more items in menu"))))
253db917
ER
1553
1554(defun Info-last-menu-item ()
1555 (interactive)
1556 (save-excursion
1557 (forward-line 1)
fdf4b680 1558 ;; Bind this in case the user sets it to nil.
760d5cb3
GM
1559 (let* ((case-fold-search t)
1560 (beg (save-excursion
1561 (and (search-backward "\n* menu:" nil t)
1562 (point)))))
0a56332b
RS
1563 (or (and beg (search-backward "\n* " beg t))
1564 (error "No previous items in menu")))
1565 (Info-goto-node (save-excursion
1566 (goto-char (match-end 0))
1567 (Info-extract-menu-node-name)))))
253db917 1568
552775bd 1569(defmacro Info-no-error (&rest body)
253db917
ER
1570 (list 'condition-case nil (cons 'progn (append body '(t))) '(error nil)))
1571
1572(defun Info-next-preorder ()
3c9179ea
RS
1573 "Go to the next subnode or the next node, or go up a level."
1574 (interactive)
1575 (cond ((Info-no-error (Info-next-menu-item)))
1576 ((Info-no-error (Info-next)))
5dab2fb4 1577 ((Info-no-error (Info-up t))
ed690657
KH
1578 ;; Since we have already gone thru all the items in this menu,
1579 ;; go up to the end of this node.
b54d0f0e
RS
1580 (goto-char (point-max))
1581 ;; Since logically we are done with the node with that menu,
1582 ;; move on from it.
1583 (Info-next-preorder))
3c9179ea
RS
1584 (t
1585 (error "No more nodes"))))
253db917
ER
1586
1587(defun Info-last-preorder ()
1588 "Go to the last node, popping up a level if there is none."
1589 (interactive)
0a56332b
RS
1590 (cond ((Info-no-error
1591 (Info-last-menu-item)
1592 ;; If we go down a menu item, go to the end of the node
1593 ;; so we can scroll back through it.
ed690657 1594 (goto-char (point-max)))
b54d0f0e
RS
1595 ;; Keep going down, as long as there are nested menu nodes.
1596 (while (Info-no-error
1597 (Info-last-menu-item)
1598 ;; If we go down a menu item, go to the end of the node
1599 ;; so we can scroll back through it.
1600 (goto-char (point-max))))
ed690657 1601 (recenter -1))
5dab2fb4
RS
1602 ((and (not (equal (Info-extract-pointer "up")
1603 (Info-extract-pointer "prev"))))
1604 (Info-no-error (Info-prev))
ed690657 1605 (goto-char (point-max))
b54d0f0e
RS
1606 (while (Info-no-error
1607 (Info-last-menu-item)
1608 ;; If we go down a menu item, go to the end of the node
1609 ;; so we can scroll back through it.
1610 (goto-char (point-max))))
ed690657 1611 (recenter -1))
5dab2fb4 1612 ((Info-no-error (Info-up t))
ed690657 1613 (goto-char (point-min))
760d5cb3
GM
1614 (let ((case-fold-search t))
1615 (or (search-forward "\n* Menu:" nil t)
1616 (goto-char (point-max)))))
ed690657 1617 (t (error "No previous nodes"))))
253db917
ER
1618
1619(defun Info-scroll-up ()
3f32dc86 1620 "Scroll one screenful forward in Info, considering all nodes as one sequence.
280d11ed
RS
1621Once you scroll far enough in a node that its menu appears on the screen
1622but after point, the next scroll moves into its first subnode.
1623
1624When you scroll past the end of a node, that goes to the next node; if
1625this node has no successor, it moves to the parent node's successor,
1626and so on. If point is inside the menu of a node, it moves to
1627subnode indicated by the following menu item. (That case won't
1628normally result from this command, but can happen in other ways.)"
1629
253db917 1630 (interactive)
0a56332b
RS
1631 (if (or (< (window-start) (point-min))
1632 (> (window-start) (point-max)))
1633 (set-window-start (selected-window) (point)))
760d5cb3
GM
1634 (let* ((case-fold-search t)
1635 (virtual-end (save-excursion
1636 (goto-char (point-min))
1637 (if (search-forward "\n* Menu:" nil t)
1638 (point)
1639 (point-max)))))
0a56332b
RS
1640 (if (or (< virtual-end (window-start))
1641 (pos-visible-in-window-p virtual-end))
1642 (Info-next-preorder)
1643 (scroll-up))))
253db917
ER
1644
1645(defun Info-scroll-down ()
3f32dc86 1646 "Scroll one screenful back in Info, considering all nodes as one sequence.
ed690657
KH
1647Within the menu of a node, this goes to its last subnode.
1648When you scroll past the beginning of a node, that goes to the
1649previous node or back up to the parent node."
253db917 1650 (interactive)
0a56332b
RS
1651 (if (or (< (window-start) (point-min))
1652 (> (window-start) (point-max)))
1653 (set-window-start (selected-window) (point)))
760d5cb3
GM
1654 (let* ((case-fold-search t)
1655 (current-point (point))
b54d0f0e
RS
1656 (virtual-end (save-excursion
1657 (beginning-of-line)
1658 (setq current-point (point))
1659 (goto-char (point-min))
1660 (search-forward "\n* Menu:"
1661 current-point
1662 t))))
0a56332b
RS
1663 (if (or virtual-end (pos-visible-in-window-p (point-min)))
1664 (Info-last-preorder)
1665 (scroll-down))))
253db917 1666
56cda6f5 1667(defun Info-next-reference (&optional recur)
552775bd
RS
1668 "Move cursor to the next cross-reference or menu item in the node."
1669 (interactive)
1670 (let ((pat "\\*note[ \n\t]*\\([^:]*\\):\\|^\\* .*:")
1bcedb3b
RS
1671 (old-pt (point))
1672 (case-fold-search t))
552775bd
RS
1673 (or (eobp) (forward-char 1))
1674 (or (re-search-forward pat nil t)
1675 (progn
1676 (goto-char (point-min))
1677 (or (re-search-forward pat nil t)
1678 (progn
1679 (goto-char old-pt)
1680 (error "No cross references in this node")))))
1681 (goto-char (match-beginning 0))
1682 (if (looking-at "\\* Menu:")
56cda6f5
RS
1683 (if recur
1684 (error "No cross references in this node")
1685 (Info-next-reference t)))))
552775bd 1686
56cda6f5 1687(defun Info-prev-reference (&optional recur)
552775bd
RS
1688 "Move cursor to the previous cross-reference or menu item in the node."
1689 (interactive)
1690 (let ((pat "\\*note[ \n\t]*\\([^:]*\\):\\|^\\* .*:")
1bcedb3b
RS
1691 (old-pt (point))
1692 (case-fold-search t))
552775bd
RS
1693 (or (re-search-backward pat nil t)
1694 (progn
1695 (goto-char (point-max))
1696 (or (re-search-backward pat nil t)
1697 (progn
1698 (goto-char old-pt)
1699 (error "No cross references in this node")))))
1700 (goto-char (match-beginning 0))
1701 (if (looking-at "\\* Menu:")
56cda6f5
RS
1702 (if recur
1703 (error "No cross references in this node")
1704 (Info-prev-reference t)))))
552775bd 1705
1143a6b0 1706(defun Info-index (topic)
fdf4b680 1707 "Look up a string TOPIC in the index for this file.
1143a6b0
ER
1708The index is defined as the first node in the top-level menu whose
1709name contains the word \"Index\", plus any immediately following
1710nodes whose names also contain the word \"Index\".
1711If there are no exact matches to the specified topic, this chooses
1712the first match which is a case-insensitive substring of a topic.
1713Use the `,' command to see the other matches.
1714Give a blank topic name to go to the Index node itself."
1715 (interactive "sIndex topic: ")
1716 (let ((orignode Info-current-node)
1717 (rnode nil)
b37daea4 1718 (pattern (format "\n\\* +\\([^\n:]*%s[^\n:]*\\):[ \t]*\\([^.\n]*\\)\\.[ \t]*\\([0-9]*\\)"
1143a6b0 1719 (regexp-quote topic)))
1bcedb3b
RS
1720 node
1721 (case-fold-search t))
1143a6b0
ER
1722 (Info-goto-node "Top")
1723 (or (search-forward "\n* menu:" nil t)
1724 (error "No index"))
1725 (or (re-search-forward "\n\\* \\(.*\\<Index\\>\\)" nil t)
1726 (error "No index"))
1727 (goto-char (match-beginning 1))
c696ac76
RS
1728 ;; Here, and subsequently in this function,
1729 ;; we bind Info-history to nil for internal node-switches
1730 ;; so that we don't put junk in the history.
1731 ;; In the first Info-goto-node call, above, we do update the history
1732 ;; because that is what the user's previous node choice into it.
1733 (let ((Info-history nil))
1143a6b0
ER
1734 (Info-goto-node (Info-extract-menu-node-name)))
1735 (or (equal topic "")
1736 (let ((matches nil)
1737 (exact nil)
c696ac76 1738 (Info-history nil)
1143a6b0
ER
1739 found)
1740 (while
1741 (progn
1742 (goto-char (point-min))
1743 (while (re-search-forward pattern nil t)
983dfbf8
SM
1744 (push (list (match-string-no-properties 1)
1745 (match-string-no-properties 2)
1746 Info-current-node
1747 (string-to-int (concat "0"
1748 (match-string 3))))
1749 matches))
1143a6b0
ER
1750 (and (setq node (Info-extract-pointer "next" t))
1751 (string-match "\\<Index\\>" node)))
1752 (Info-goto-node node))
1753 (or matches
1754 (progn
81e14cb2 1755 (Info-goto-node orignode)
920bdaab 1756 (error "No `%s' in index" topic)))
1143a6b0
ER
1757 ;; Here it is a feature that assoc is case-sensitive.
1758 (while (setq found (assoc topic matches))
1759 (setq exact (cons found exact)
1760 matches (delq found matches)))
1761 (setq Info-index-alternatives (nconc exact (nreverse matches)))
1762 (Info-index-next 0)))))
1763
1764(defun Info-index-next (num)
1765 "Go to the next matching index item from the last `i' command."
1766 (interactive "p")
1767 (or Info-index-alternatives
882e61bf 1768 (error "No previous `i' command"))
1143a6b0
ER
1769 (while (< num 0)
1770 (setq num (+ num (length Info-index-alternatives))))
1771 (while (> num 0)
1772 (setq Info-index-alternatives
1773 (nconc (cdr Info-index-alternatives)
1774 (list (car Info-index-alternatives)))
1775 num (1- num)))
1776 (Info-goto-node (nth 1 (car Info-index-alternatives)))
1777 (if (> (nth 3 (car Info-index-alternatives)) 0)
1778 (forward-line (nth 3 (car Info-index-alternatives)))
760d5cb3 1779 (forward-line 3) ; don't search in headers
1143a6b0 1780 (let ((name (car (car Info-index-alternatives))))
920bdaab
RS
1781 (Info-find-index-name name)))
1782 (message "Found `%s' in %s. %s"
1143a6b0
ER
1783 (car (car Info-index-alternatives))
1784 (nth 2 (car Info-index-alternatives))
1785 (if (cdr Info-index-alternatives)
1786 "(Press `,' for more)"
1787 "(Only match)")))
1788
920bdaab
RS
1789(defun Info-find-index-name (name)
1790 "Move point to the place within the current node where NAME is defined."
1bcedb3b
RS
1791 (let ((case-fold-search t))
1792 (if (or (re-search-forward (format
1793 "[a-zA-Z]+: %s\\( \\|$\\)"
1794 (regexp-quote name)) nil t)
1795 (search-forward (format "`%s'" name) nil t)
1796 (and (string-match "\\`.*\\( (.*)\\)\\'" name)
1797 (search-forward
1798 (format "`%s'" (substring name 0 (match-beginning 1)))
1799 nil t))
1800 (search-forward name nil t))
1801 (beginning-of-line)
1802 (goto-char (point-min)))))
920bdaab 1803
a384cab3
JB
1804(defun Info-undefined ()
1805 "Make command be undefined in Info."
1806 (interactive)
1807 (ding))
1808
1809(defun Info-help ()
1810 "Enter the Info tutorial."
1811 (interactive)
1812 (delete-other-windows)
1813 (Info-find-node "info"
1814 (if (< (window-height) 23)
1815 "Help-Small-Screen"
1816 "Help")))
1817
1818(defun Info-summary ()
1819 "Display a brief summary of all Info commands."
1820 (interactive)
1821 (save-window-excursion
1822 (switch-to-buffer "*Help*")
881c84c7 1823 (setq buffer-read-only nil)
a384cab3
JB
1824 (erase-buffer)
1825 (insert (documentation 'Info-mode))
9d29f94c 1826 (help-mode)
a384cab3
JB
1827 (goto-char (point-min))
1828 (let (ch flag)
1829 (while (progn (setq flag (not (pos-visible-in-window-p (point-max))))
1830 (message (if flag "Type Space to see more"
1831 "Type Space to return to Info"))
1614c867 1832 (if (not (eq ?\ (setq ch (read-event))))
dbc4e1c1 1833 (progn (setq unread-command-events (list ch)) nil)
a384cab3 1834 flag))
552775bd
RS
1835 (scroll-up)))
1836 (bury-buffer "*Help*")))
a384cab3
JB
1837\f
1838(defun Info-get-token (pos start all &optional errorstring)
fdf4b680 1839 "Return the token around POS.
a384cab3
JB
1840POS must be somewhere inside the token
1841START is a regular expression which will match the
1842 beginning of the tokens delimited string
1843ALL is a regular expression with a single
90a715f0 1844 parenthesized subpattern which is the token to be
fdf4b680 1845 returned. E.g. '{\(.*\)}' would return any string
a384cab3 1846 enclosed in braces around POS.
fdf4b680 1847ERRORSTRING optional fourth argument, controls action on no match
a384cab3
JB
1848 nil: return nil
1849 t: beep
1850 a string: signal an error, using that string."
1bcedb3b
RS
1851 (let ((case-fold-search t))
1852 (save-excursion
1853 (goto-char pos)
1854 ;; First look for a match for START that goes across POS.
1855 (while (and (not (bobp)) (> (point) (- pos (length start)))
1856 (not (looking-at start)))
1857 (forward-char -1))
1858 ;; If we did not find one, search back for START
1859 ;; (this finds only matches that end at or before POS).
1860 (or (looking-at start)
1861 (progn
1862 (goto-char pos)
1863 (re-search-backward start (max (point-min) (- pos 200)) 'yes)))
1864 (let (found)
1865 (while (and (re-search-forward all (min (point-max) (+ pos 200)) 'yes)
1866 (not (setq found (and (<= (match-beginning 0) pos)
1867 (> (match-end 0) pos))))))
1868 (if (and found (<= (match-beginning 0) pos)
1869 (> (match-end 0) pos))
110d4e80 1870 (match-string-no-properties 1)
1bcedb3b
RS
1871 (cond ((null errorstring)
1872 nil)
1873 ((eq errorstring t)
1874 (beep)
1875 nil)
1876 (t
1877 (error "No %s around position %d" errorstring pos))))))))
a384cab3 1878
981947af 1879(defun Info-mouse-follow-nearest-node (click)
f9969361
RS
1880 "\\<Info-mode-map>Follow a node reference near point.
1881Like \\[Info-menu], \\[Info-follow-reference], \\[Info-next], \\[Info-prev] or \\[Info-up] command, depending on where you click.
981947af 1882At end of the node's text, moves to the next node, or up if none."
f9969361 1883 (interactive "e")
d1268e52 1884 (mouse-set-point click)
981947af
KH
1885 (and (not (Info-try-follow-nearest-node))
1886 (save-excursion (forward-line 1) (eobp))
ed690657 1887 (Info-next-preorder)))
981947af
KH
1888
1889(defun Info-follow-nearest-node ()
1890 "\\<Info-mode-map>Follow a node reference near point.
1891Like \\[Info-menu], \\[Info-follow-reference], \\[Info-next], \\[Info-prev] or \\[Info-up] command, depending on where point is.
1892If no reference to follow, moves to the next node, or up if none."
1893 (interactive)
1894 (or (Info-try-follow-nearest-node)
ed690657 1895 (Info-next-preorder)))
981947af
KH
1896
1897;; Common subroutine.
1898(defun Info-try-follow-nearest-node ()
1899 "Follow a node reference near point. Return non-nil if successful."
a384cab3
JB
1900 (let (node)
1901 (cond
981947af
KH
1902 ((setq node (Info-get-token (point) "\\*note[ \n]"
1903 "\\*note[ \n]\\([^:]*\\):"))
a384cab3 1904 (Info-follow-reference node))
b37daea4 1905 ((setq node (Info-get-token (point) "\\* +" "\\* +\\([^:]*\\)::"))
a384cab3 1906 (Info-goto-node node))
631ba13e
RS
1907 ((Info-get-token (point) "\\* +" "\\* +\\([^:]*\\):")
1908 (beginning-of-line)
1909 (forward-char 2)
1910 (setq node (Info-extract-menu-node-name))
1911 (Info-goto-node node))
bc2ada62 1912 ((setq node (Info-get-token (point) "Up: " "Up: \\([^,\n\t]*\\)"))
a384cab3 1913 (Info-goto-node node))
bc2ada62 1914 ((setq node (Info-get-token (point) "Next: " "Next: \\([^,\n\t]*\\)"))
a384cab3 1915 (Info-goto-node node))
bc2ada62 1916 ((setq node (Info-get-token (point) "File: " "File: \\([^,\n\t]*\\)"))
a384cab3 1917 (Info-goto-node "Top"))
bc2ada62 1918 ((setq node (Info-get-token (point) "Prev: " "Prev: \\([^,\n\t]*\\)"))
981947af
KH
1919 (Info-goto-node node)))
1920 node))
a384cab3
JB
1921\f
1922(defvar Info-mode-map nil
1923 "Keymap containing Info commands.")
1924(if Info-mode-map
1925 nil
1926 (setq Info-mode-map (make-keymap))
1927 (suppress-keymap Info-mode-map)
1928 (define-key Info-mode-map "." 'beginning-of-buffer)
253db917 1929 (define-key Info-mode-map " " 'Info-scroll-up)
981947af 1930 (define-key Info-mode-map "\C-m" 'Info-follow-nearest-node)
552775bd
RS
1931 (define-key Info-mode-map "\t" 'Info-next-reference)
1932 (define-key Info-mode-map "\e\t" 'Info-prev-reference)
e38e7367
RM
1933 (define-key Info-mode-map "1" 'Info-nth-menu-item)
1934 (define-key Info-mode-map "2" 'Info-nth-menu-item)
1935 (define-key Info-mode-map "3" 'Info-nth-menu-item)
1936 (define-key Info-mode-map "4" 'Info-nth-menu-item)
1937 (define-key Info-mode-map "5" 'Info-nth-menu-item)
1938 (define-key Info-mode-map "6" 'Info-nth-menu-item)
1939 (define-key Info-mode-map "7" 'Info-nth-menu-item)
1940 (define-key Info-mode-map "8" 'Info-nth-menu-item)
1941 (define-key Info-mode-map "9" 'Info-nth-menu-item)
82a4c008 1942 (define-key Info-mode-map "0" 'undefined)
a384cab3
JB
1943 (define-key Info-mode-map "?" 'Info-summary)
1944 (define-key Info-mode-map "]" 'Info-forward-node)
1945 (define-key Info-mode-map "[" 'Info-backward-node)
1946 (define-key Info-mode-map "<" 'Info-top-node)
1947 (define-key Info-mode-map ">" 'Info-final-node)
1948 (define-key Info-mode-map "b" 'beginning-of-buffer)
1949 (define-key Info-mode-map "d" 'Info-directory)
1950 (define-key Info-mode-map "e" 'Info-edit)
1951 (define-key Info-mode-map "f" 'Info-follow-reference)
1952 (define-key Info-mode-map "g" 'Info-goto-node)
1953 (define-key Info-mode-map "h" 'Info-help)
1143a6b0 1954 (define-key Info-mode-map "i" 'Info-index)
a384cab3
JB
1955 (define-key Info-mode-map "l" 'Info-last)
1956 (define-key Info-mode-map "m" 'Info-menu)
1957 (define-key Info-mode-map "n" 'Info-next)
1958 (define-key Info-mode-map "p" 'Info-prev)
1959 (define-key Info-mode-map "q" 'Info-exit)
1960 (define-key Info-mode-map "s" 'Info-search)
47fc33ca
RS
1961 ;; For consistency with Rmail.
1962 (define-key Info-mode-map "\M-s" 'Info-search)
4fceda3c 1963 (define-key Info-mode-map "\M-n" 'clone-buffer)
db0c9809 1964 (define-key Info-mode-map "t" 'Info-top-node)
a384cab3 1965 (define-key Info-mode-map "u" 'Info-up)
1143a6b0 1966 (define-key Info-mode-map "," 'Info-index-next)
803eaf50 1967 (define-key Info-mode-map "\177" 'Info-scroll-down)
981947af 1968 (define-key Info-mode-map [mouse-2] 'Info-mouse-follow-nearest-node)
aea2a8da 1969 )
75a209d4
RS
1970
1971(defun Info-check-pointer (item)
fdf4b680 1972 "Non-nil if ITEM is present in this node."
75a209d4
RS
1973 (condition-case nil
1974 (Info-extract-pointer item)
1975 (error nil)))
1976
d4af987a
DL
1977(easy-menu-define
1978 Info-mode-menu Info-mode-map
1979 "Menu for info files."
1980 '("Info"
1981 ["Up" Info-up :active (Info-check-pointer "up")
1982 :help "Go up in the Info tree"]
1983 ["Next" Info-next :active (Info-check-pointer "next")
1984 :help "Go to the next node"]
1985 ["Previous" Info-prev :active (Info-check-pointer "prev[ious]*")
1986 :help "Go to the previous node"]
1987 ["Backward" Info-backward-node
1988 :help "Go backward one node, considering all as a sequence"]
1989 ["Forward" Info-forward-node
1990 :help "Go forward one node, considering all as a sequence"]
7210b6f5
DL
1991 ["Beginning" beginning-of-buffer
1992 :help "Go to beginning of this node"]
d4af987a
DL
1993 ["Top" Info-top-node
1994 :help "Go to top node of file"]
1995 ["Final Node" Info-final-node
1996 :help "Go to final node in this file"]
1997 ("Menu Item" ["You should never see this" report-emacs-bug t])
1998 ("Reference" ["You should never see this" report-emacs-bug t])
1999 ["Search..." Info-search
2000 :help "Search for regular expression in this Info file"]
d1268e52 2001 ["Go to Node..." Info-goto-node
d4af987a 2002 :help "Go to a named node"]
ec03f31e 2003 ["Last" Info-last :active Info-history
d4af987a
DL
2004 :help "Go to the last node you were at"]
2005 ("Index..."
2006 ["Lookup a String" Info-index
2007 :help "Look for a string in the index items"]
2008 ["Next Matching Item" Info-index-next
2009 :help "Look for another occurrence of previous item"])
7210b6f5
DL
2010 ["Edit" Info-edit :help "Edit contents of this node"
2011 :active Info-enable-edit]
ec03f31e 2012 ["Exit" Info-exit :help "Stop reading Info"]))
75a209d4 2013
7210b6f5
DL
2014
2015(defvar info-tool-bar-map
2016 (if (display-graphic-p)
2017 (let ((tool-bar-map (make-sparse-keymap)))
2018 (tool-bar-add-item-from-menu 'Info-exit "close" Info-mode-map)
2019 (tool-bar-add-item-from-menu 'Info-prev "left_arrow" Info-mode-map)
2020 (tool-bar-add-item-from-menu 'Info-next "right_arrow" Info-mode-map)
2021 (tool-bar-add-item-from-menu 'Info-up "up_arrow" Info-mode-map)
ec03f31e 2022 (tool-bar-add-item-from-menu 'Info-last "undo" Info-mode-map)
7210b6f5
DL
2023 (tool-bar-add-item-from-menu 'Info-top-node "home" Info-mode-map)
2024 (tool-bar-add-item-from-menu 'Info-index "index" Info-mode-map)
2025 (tool-bar-add-item-from-menu 'Info-goto-node "jump_to" Info-mode-map)
2026 (tool-bar-add-item-from-menu 'Info-search "search" Info-mode-map)
2027 tool-bar-map)))
2028
75a209d4
RS
2029(defvar Info-menu-last-node nil)
2030;; Last node the menu was created for.
6d2c8e3e 2031;; Value is a list, (FILE-NAME NODE-NAME).
75a209d4
RS
2032
2033(defun Info-menu-update ()
fdf4b680 2034 "Update the Info menu for the current node."
75a209d4
RS
2035 (condition-case nil
2036 (if (or (not (eq major-mode 'Info-mode))
6d2c8e3e
RS
2037 (equal (list Info-current-file Info-current-node)
2038 Info-menu-last-node))
75a209d4
RS
2039 ()
2040 ;; Update menu menu.
2041 (let* ((Info-complete-menu-buffer (current-buffer))
2042 (items (nreverse (condition-case nil
2043 (Info-complete-menu-item
2044 "" (lambda (e) t) t)
2045 (error nil))))
399c88ad 2046 entries current
75a209d4
RS
2047 (number 0))
2048 (while (and items (< number 9))
2049 (setq current (car items)
2050 items (cdr items)
2051 number (1+ number))
399c88ad 2052 (setq entries (cons `[,current
75a209d4
RS
2053 (Info-menu ,current)
2054 :keys ,(format "%d" number)]
2055 entries)))
2056 (if items
2057 (setq entries (cons ["Other..." Info-menu t] entries)))
2058 (or entries
2059 (setq entries (list ["No menu" nil nil])))
d4af987a 2060 (easy-menu-change '("Info") "Menu Item" (nreverse entries)))
75a209d4
RS
2061 ;; Update reference menu. Code stolen from `Info-follow-reference'.
2062 (let ((items nil)
399c88ad 2063 str i entries current
1bcedb3b
RS
2064 (number 0)
2065 (case-fold-search t))
75a209d4
RS
2066 (save-excursion
2067 (goto-char (point-min))
2068 (while (re-search-forward "\\*note[ \n\t]*\\([^:]*\\):" nil t)
2069 (setq str (buffer-substring
2070 (match-beginning 1)
2071 (1- (point))))
2072 (setq i 0)
2073 (while (setq i (string-match "[ \n\t]+" str i))
2074 (setq str (concat (substring str 0 i) " "
2075 (substring str (match-end 0))))
2076 (setq i (1+ i)))
2077 (setq items
2078 (cons str items))))
2079 (while (and items (< number 9))
2080 (setq current (car items)
2081 items (cdr items)
2082 number (1+ number))
399c88ad 2083 (setq entries (cons `[,current
75a209d4
RS
2084 (Info-follow-reference ,current)
2085 t]
2086 entries)))
2087 (if items
2088 (setq entries (cons ["Other..." Info-follow-reference t]
2089 entries)))
2090 (or entries
2091 (setq entries (list ["No references" nil nil])))
2092 (easy-menu-change '("Info") "Reference" (nreverse entries)))
2093 ;; Update last seen node.
6d2c8e3e 2094 (setq Info-menu-last-node (list Info-current-file Info-current-node)))
75a209d4
RS
2095 ;; Try to avoid entering infinite beep mode in case of errors.
2096 (error (ding))))
2097
a384cab3
JB
2098\f
2099;; Info mode is suitable only for specially formatted data.
68bc119c 2100(put 'Info-mode 'mode-class 'special)
a384cab3
JB
2101
2102(defun Info-mode ()
fdf4b680 2103 "Info mode provides commands for browsing through the Info documentation tree.
a384cab3
JB
2104Documentation in Info is divided into \"nodes\", each of which discusses
2105one topic and contains references to other nodes which discuss related
2106topics. Info has commands to follow the references and show you other nodes.
2107
fdf4b680 2108\\<Info-mode-map>\
a384cab3 2109\\[Info-help] Invoke the Info tutorial.
6b136ab9 2110\\[Info-exit] Quit Info: reselect previously selected buffer.
a384cab3
JB
2111
2112Selecting other nodes:
21d5959f
RS
2113\\[Info-mouse-follow-nearest-node]
2114 Follow a node reference you click on.
2115 This works with menu items, cross references, and
2116 the \"next\", \"previous\" and \"up\", depending on where you click.
6b136ab9 2117\\[Info-follow-nearest-node] Follow a node reference near point, like \\[Info-mouse-follow-nearest-node].
a384cab3 2118\\[Info-next] Move to the \"next\" node of this node.
bc2ada62 2119\\[Info-prev] Move to the \"previous\" node of this node.
a384cab3
JB
2120\\[Info-up] Move \"up\" from this node.
2121\\[Info-menu] Pick menu item specified by name (or abbreviation).
2122 Picking a menu item causes another node to be selected.
aea2a8da 2123\\[Info-directory] Go to the Info directory node.
a384cab3
JB
2124\\[Info-follow-reference] Follow a cross reference. Reads name of reference.
2125\\[Info-last] Move to the last node you were at.
1143a6b0
ER
2126\\[Info-index] Look up a topic in this file's Index and move to that node.
2127\\[Info-index-next] (comma) Move to the next match from a previous `i' command.
6b136ab9
DL
2128\\[Info-top-node] Go to the Top node of this file.
2129\\[Info-final-node] Go to the final node in this file.
2130\\[Info-backward-node] Go backward one node, considering all nodes as forming one sequence.
2131\\[Info-forward-node] Go forward one node, considering all nodes as forming one sequence.
a384cab3
JB
2132
2133Moving within a node:
177c3549
RS
2134\\[Info-scroll-up] Normally, scroll forward a full screen.
2135Once you scroll far enough in a node that its menu appears on the screen
2136but after point, the next scroll moves into its first subnode.
2137When after all menu items (or if their is no menu), move up to
2138the parent node.
2139\\[Info-scroll-down] Normally, scroll backward. If the beginning of the buffer is
803eaf50 2140already visible, try to go to the previous menu entry, or up if there is none.
399c88ad 2141\\[beginning-of-buffer] Go to beginning of node.
a384cab3 2142
a384cab3
JB
2143Advanced commands:
2144\\[Info-exit] Quit Info: reselect previously selected buffer.
2145\\[Info-edit] Edit contents of selected node.
21461 Pick first item in node's menu.
21472, 3, 4, 5 Pick second ... fifth item in node's menu.
2148\\[Info-goto-node] Move to node specified by name.
2149 You may include a filename as well, as (FILENAME)NODENAME.
552775bd 2150\\[universal-argument] \\[info] Move to new Info file with completion.
a384cab3 2151\\[Info-search] Search through this Info file for specified regexp,
803eaf50 2152 and select the node in which the next occurrence is found.
552775bd
RS
2153\\[Info-next-reference] Move cursor to next cross-reference or menu item.
2154\\[Info-prev-reference] Move cursor to previous cross-reference or menu item."
a384cab3
JB
2155 (kill-all-local-variables)
2156 (setq major-mode 'Info-mode)
2157 (setq mode-name "Info")
f73299f3 2158 (setq tab-width 8)
a384cab3 2159 (use-local-map Info-mode-map)
75a209d4 2160 (add-hook 'activate-menubar-hook 'Info-menu-update nil t)
a384cab3
JB
2161 (set-syntax-table text-mode-syntax-table)
2162 (setq local-abbrev-table text-mode-abbrev-table)
2163 (setq case-fold-search t)
2164 (setq buffer-read-only t)
a384cab3
JB
2165 (make-local-variable 'Info-current-file)
2166 (make-local-variable 'Info-current-subfile)
2167 (make-local-variable 'Info-current-node)
2168 (make-local-variable 'Info-tag-table-marker)
c5fe2ff1
RS
2169 (setq Info-tag-table-marker (make-marker))
2170 (make-local-variable 'Info-tag-table-buffer)
2171 (setq Info-tag-table-buffer nil)
a384cab3 2172 (make-local-variable 'Info-history)
1143a6b0 2173 (make-local-variable 'Info-index-alternatives)
7210b6f5 2174 (set (make-local-variable 'tool-bar-map) info-tool-bar-map)
93480d70
RS
2175 ;; This is for the sake of the invisible text we use handling titles.
2176 (make-local-variable 'line-move-ignore-invisible)
2177 (setq line-move-ignore-invisible t)
b7aeabeb 2178 (add-hook 'clone-buffer-hook 'Info-clone-buffer-hook nil t)
a384cab3
JB
2179 (Info-set-mode-line)
2180 (run-hooks 'Info-mode-hook))
2181
4fceda3c
SM
2182(defun Info-clone-buffer-hook ()
2183 (when (bufferp Info-tag-table-buffer)
2184 (setq Info-tag-table-buffer
2185 (with-current-buffer Info-tag-table-buffer (clone-buffer)))
2186 (let ((m Info-tag-table-marker))
2187 (when (and (markerp m) (marker-position m))
2188 (setq Info-tag-table-marker
2189 (with-current-buffer Info-tag-table-buffer
2190 (copy-marker (marker-position m))))))))
2191
7210b6f5
DL
2192(defvar Info-edit-map (let ((map (make-sparse-keymap)))
2193 (set-keymap-parent map text-mode-map)
2194 (define-key map "\C-c\C-c" 'Info-cease-edit)
2195 map)
a384cab3 2196 "Local keymap used within `e' command of Info.")
a384cab3
JB
2197
2198;; Info-edit mode is suitable only for specially formatted data.
68bc119c 2199(put 'Info-edit-mode 'mode-class 'special)
a384cab3
JB
2200
2201(defun Info-edit-mode ()
2202 "Major mode for editing the contents of an Info node.
a426b157 2203Like text mode with the addition of `Info-cease-edit'
a384cab3
JB
2204which returns to Info mode for browsing.
2205\\{Info-edit-map}"
a384cab3
JB
2206 (use-local-map Info-edit-map)
2207 (setq major-mode 'Info-edit-mode)
2208 (setq mode-name "Info Edit")
2209 (kill-local-variable 'mode-line-buffer-identification)
2210 (setq buffer-read-only nil)
2c609f53 2211 (force-mode-line-update)
e82c28f9
KH
2212 (buffer-enable-undo (current-buffer))
2213 (run-hooks 'Info-edit-mode-hook))
2214
2215(defun Info-edit ()
2216 "Edit the contents of this Info node.
2217Allowed only if variable `Info-enable-edit' is non-nil."
2218 (interactive)
2219 (or Info-enable-edit
2220 (error "Editing info nodes is not enabled"))
2221 (Info-edit-mode)
8ab3e50b 2222 (message "%s" (substitute-command-keys
760d5cb3 2223 "Editing: Type \\<Info-edit-map>\\[Info-cease-edit] to return to info")))
a384cab3
JB
2224
2225(defun Info-cease-edit ()
2226 "Finish editing Info node; switch back to Info proper."
2227 (interactive)
2228 ;; Do this first, so nothing has changed if user C-g's at query.
2229 (and (buffer-modified-p)
2230 (y-or-n-p "Save the file? ")
2231 (save-buffer))
2232 (use-local-map Info-mode-map)
2233 (setq major-mode 'Info-mode)
2234 (setq mode-name "Info")
2235 (Info-set-mode-line)
2236 (setq buffer-read-only t)
2c609f53 2237 (force-mode-line-update)
a384cab3
JB
2238 (and (marker-position Info-tag-table-marker)
2239 (buffer-modified-p)
2240 (message "Tags may have changed. Use Info-tagify if necessary")))
f0a8a3f1 2241\f
f88ab1c9 2242(defvar Info-file-list-for-emacs
dd31e4e8 2243 '("ediff" "forms" "gnus" ("mh" . "mh-e") "sc" "message"
af718538
EZ
2244 ("dired" . "dired-x") ("c" . "ccmode") "viper" "vip"
2245 ("skeleton" . "autotype") ("auto-insert" . "autotype")
2246 ("copyright" . "autotype") ("executable" . "autotype")
2247 ("time-stamp" . "autotype") ("quickurl" . "autotype")
2248 ("tempo" . "autotype") ("hippie-expand" . "autotype")
2249 ("cvs" . "pcl-cvs")
379b70e7 2250 "ebrowse" "eshell" "cl" "idlwave" "reftex" "speedbar" "widget" "woman")
f88ab1c9
RS
2251 "List of Info files that describe Emacs commands.
2252An element can be a file name, or a list of the form (PREFIX . FILE)
2253where PREFIX is a name prefix and FILE is the file to look in.
2254If the element is just a file name, the file name also serves as the prefix.")
2255
f0a8a3f1 2256(defun Info-find-emacs-command-nodes (command)
f88ab1c9 2257 "Return a list of locations documenting COMMAND.
f57b2cd8
RS
2258The `info-file' property of COMMAND says which Info manual to search.
2259If COMMAND has no property, the variable `Info-file-list-for-emacs'
2260defines heuristics for which Info manual to try.
fdf4b680 2261The locations are of the format used in `Info-history', i.e.
f0a8a3f1 2262\(FILENAME NODENAME BUFFERPOS\)."
f0a8a3f1 2263 (let ((where '())
b37daea4 2264 (cmd-desc (concat "^\\* +" (regexp-quote (symbol-name command))
218c2cc7 2265 "\\( <[0-9]+>\\)?:\\s *\\(.*\\)\\.$"))
f88ab1c9
RS
2266 (info-file "emacs")) ;default
2267 ;; Determine which info file this command is documented in.
2268 (if (get command 'info-file)
2269 (setq info-file (get command 'info-file))
2270 ;; If it doesn't say explicitly, test its name against
2271 ;; various prefixes that we know.
2272 (let ((file-list Info-file-list-for-emacs))
2273 (while file-list
2274 (let* ((elt (car file-list))
2275 (name (if (consp elt)
2276 (car elt)
2277 elt))
2278 (file (if (consp elt) (cdr elt) elt))
f57b2cd8 2279 (regexp (concat "\\`" (regexp-quote name)
f88ab1c9
RS
2280 "\\(\\'\\|-\\)")))
2281 (if (string-match regexp (symbol-name command))
2282 (setq info-file file file-list nil))
2283 (setq file-list (cdr file-list))))))
218c2cc7
EZ
2284 (Info-find-node info-file "Top")
2285 (or (and (search-forward "\n* menu:" nil t)
2286 (re-search-forward "\n\\* \\(.*\\<Index\\>\\)" nil t))
2287 (error "Info file `%s' appears to lack an index" info-file))
2288 (goto-char (match-beginning 1))
2289 ;; Bind Info-history to nil, to prevent the index nodes from
2290 ;; getting into the node history.
2291 (let ((Info-history nil)
2292 (exact nil)
2293 node found)
2294 (Info-goto-node (Info-extract-menu-node-name))
2295 (while
2296 (progn
2297 (goto-char (point-min))
2298 (while (re-search-forward cmd-desc nil t)
2299 (setq where
2300 (cons (list Info-current-file
2301 (match-string-no-properties 2)
760d5cb3
GM
2302 0)
2303 where)))
218c2cc7
EZ
2304 (and (setq node (Info-extract-pointer "next" t))
2305 (string-match "\\<Index\\>" node)))
2306 (Info-goto-node node)))
2307 where))
f0a8a3f1
RM
2308
2309;;;###autoload
2310(defun Info-goto-emacs-command-node (command)
e0568e86 2311 "Go to the Info node in the Emacs manual for command COMMAND.
f88ab1c9 2312The command is found by looking up in Emacs manual's Command Index
f57b2cd8
RS
2313or in another manual found via COMMAND's `info-file' property or
2314the variable `Info-file-list-for-emacs'."
f0a8a3f1
RM
2315 (interactive "CFind documentation for command: ")
2316 (or (commandp command)
2317 (signal 'wrong-type-argument (list 'commandp command)))
2318 (let ((where (Info-find-emacs-command-nodes command)))
2319 (if where
2320 (let ((num-matches (length where)))
2321 ;; Get Info running, and pop to it in another window.
2322 (save-window-excursion
2323 (info))
c5fe2ff1
RS
2324 ;; FIXME It would be cool if this could use a buffer other
2325 ;; than *info*.
f0a8a3f1 2326 (pop-to-buffer "*info*")
218c2cc7
EZ
2327 ;; Bind Info-history to nil, to prevent the last Index node
2328 ;; visited by Info-find-emacs-command-nodes from being
2329 ;; pushed onto the history.
2330 (let ((Info-history nil))
2331 (Info-find-node (car (car where))
2332 (car (cdr (car where)))))
f0a8a3f1
RM
2333 (if (> num-matches 1)
2334 (progn
218c2cc7
EZ
2335 ;; (car where) will be pushed onto Info-history
2336 ;; when/if they go to another node. Put the other
2337 ;; nodes that were found on the history.
f0a8a3f1 2338 (setq Info-history (nconc (cdr where) Info-history))
8ab3e50b 2339 (message "Found %d other entr%s. Use %s to see %s."
cedb118c
RS
2340 (1- num-matches)
2341 (if (> num-matches 2) "ies" "y")
8ab3e50b 2342 (substitute-command-keys "\\[Info-last]")
cedb118c 2343 (if (> num-matches 2) "them" "it")))))
e9b81433 2344 (error "Couldn't find documentation for %s" command))))
f0a8a3f1
RM
2345
2346;;;###autoload
2347(defun Info-goto-emacs-key-command-node (key)
2348 "Go to the Info node in the Emacs manual the command bound to KEY, a string.
fdf4b680 2349Interactively, if the binding is `execute-extended-command', a command is read.
f88ab1c9 2350The command is found by looking up in Emacs manual's Command Index
f57b2cd8
RS
2351or in another manual found via COMMAND's `info-file' property or
2352the variable `Info-file-list-for-emacs'."
ea33749b 2353 (interactive "kFind documentation for key: ")
f0a8a3f1
RM
2354 (let ((command (key-binding key)))
2355 (cond ((null command)
9e5c2f50 2356 (message "%s is undefined" (key-description key)))
f0a8a3f1
RM
2357 ((and (interactive-p)
2358 (eq command 'execute-extended-command))
2359 (Info-goto-emacs-command-node
2360 (read-command "Find documentation for command: ")))
2361 (t
2362 (Info-goto-emacs-command-node command)))))
552775bd 2363\f
bbed5c3f 2364(defface Info-title-1-face
b250866f 2365 '((((type tty pc) (class color)) (:foreground "yellow" :weight bold))
333cd59e 2366 (t (:height 1.2 :inherit Info-title-2-face)))
bbed5c3f
GM
2367 "Face for Info titles at level 1."
2368 :group 'info)
2369
2370(defface Info-title-2-face
b250866f 2371 '((((type tty pc) (class color)) (:foreground "lightblue" :weight bold))
333cd59e 2372 (t (:height 1.2 :inherit Info-title-3-face)))
bbed5c3f
GM
2373 "Face for Info titles at level 2."
2374 :group 'info)
2375
2376(defface Info-title-3-face
b250866f 2377 '((((type tty pc) (class color)) (:weight bold))
a8b883c2 2378 (t (:height 1.2 :inherit Info-title-4-face)))
bbed5c3f
GM
2379 "Face for Info titles at level 3."
2380 :group 'info)
2381
a8b883c2
MB
2382(defface Info-title-4-face
2383 '((((type tty pc) (class color)) (:weight bold))
2384 (t (:weight bold :inherit variable-pitch)))
2385 "Face for Info titles at level 4."
2386 :group 'info)
2387
973a3104
MB
2388(defface info-menu-header
2389 '((((type tty pc))
42121c23
SZ
2390 :underline t
2391 :weight bold)
973a3104 2392 (t
42121c23
SZ
2393 :inherit variable-pitch
2394 :weight bold))
973a3104
MB
2395 "Face for headers in Info menus."
2396 :group 'info)
2397
2398(defun Info-fontify-menu-headers ()
2399 "Add the face `info-menu-header' to any header before a menu entry."
2400 (save-excursion
2401 (goto-char (point-min))
2402 (when (re-search-forward "\\* Menu:" nil t)
2403 (put-text-property (match-beginning 0) (match-end 0)
2404 'face 'info-menu-header)
2405 (while (re-search-forward "\n\n\\([^*\n ].*\\)\n\n?[*]" nil t)
2406 (put-text-property (match-beginning 1) (match-end 1)
2407 'face 'info-menu-header)))))
2408
552775bd
RS
2409(defun Info-fontify-node ()
2410 (save-excursion
1bcedb3b
RS
2411 (let ((buffer-read-only nil)
2412 (case-fold-search t))
552775bd 2413 (goto-char (point-min))
35d2d241
RS
2414 (when (looking-at "^File: [^,: \t]+,?[ \t]+")
2415 (goto-char (match-end 0))
50ac70af 2416 (while (looking-at "[ \t]*\\([^:, \t\n]+\\):[ \t]+\\([^:,\t\n]+\\),?")
35d2d241 2417 (goto-char (match-end 0))
50ac70af
MB
2418 (let* ((nbeg (match-beginning 2))
2419 (nend (match-end 2))
2420 (tbeg (match-beginning 1))
2421 (tag (buffer-substring tbeg (match-end 1))))
2422 (if (string-equal tag "Node")
2423 (put-text-property nbeg nend 'face 'info-header-node)
2424 (put-text-property nbeg nend 'face 'info-header-xref)
2425 (put-text-property nbeg nend 'mouse-face 'highlight)
2426 (put-text-property tbeg nend
2427 'help-echo
d1268e52 2428 (concat "Go to node "
50ac70af
MB
2429 (buffer-substring nbeg nend)))
2430 (let ((fun (cdr (assoc tag '(("Prev" . Info-prev)
2431 ("Next" . Info-next)
2432 ("Up" . Info-up))))))
2433 (when fun
2434 (let ((keymap (make-sparse-keymap)))
2435 (define-key keymap [header-line mouse-1] fun)
2436 (define-key keymap [header-line mouse-2] fun)
2437 (put-text-property tbeg nend 'local-map keymap))))
2438 ))))
552775bd 2439 (goto-char (point-min))
a8b883c2 2440 (while (re-search-forward "\n\\([^ \t\n].+\\)\n\\(\\*+\\|=+\\|-+\\|\\.+\\)$"
760d5cb3 2441 nil t)
bbed5c3f
GM
2442 (let ((c (preceding-char))
2443 face)
2444 (cond ((= c ?*) (setq face 'Info-title-1-face))
2445 ((= c ?=) (setq face 'Info-title-2-face))
a8b883c2
MB
2446 ((= c ?-) (setq face 'Info-title-3-face))
2447 (t (setq face 'Info-title-4-face)))
bbed5c3f
GM
2448 (put-text-property (match-beginning 1) (match-end 1)
2449 'face face))
93480d70
RS
2450 ;; This is a serious problem for trying to handle multiple
2451 ;; frame types at once. We want this text to be invisible
2452 ;; on frames that can display the font above.
1a578e9b 2453 (if (memq (framep (selected-frame)) '(x pc w32 mac))
a198aa54 2454 (add-text-properties (match-end 1) (match-end 2)
0e8c11d8 2455 '(invisible t intangible t))))
98d8273c 2456 (goto-char (point-min))
7757c476 2457 (while (re-search-forward "\\*Note[ \n\t]+\\([^:]*\\):" nil t)
552775bd
RS
2458 (if (= (char-after (1- (match-beginning 0))) ?\") ; hack
2459 nil
d1268e52
DL
2460 (add-text-properties (match-beginning 1) (match-end 1)
2461 '(face info-xref
2462 mouse-face highlight
2463 help-echo "mouse-2: go to this node"))))
552775bd
RS
2464 (goto-char (point-min))
2465 (if (and (search-forward "\n* Menu:" nil t)
2466 (not (string-match "\\<Index\\>" Info-current-node))
2467 ;; Don't take time to annotate huge menus
34a0a4ee 2468 (< (- (point-max) (point)) Info-fontify-maximum-menu-size))
552775bd 2469 (let ((n 0))
b37daea4 2470 (while (re-search-forward "^\\* +\\([^:\t\n]*\\):" nil t)
552775bd 2471 (setq n (1+ n))
760d5cb3 2472 (if (memq n '(5 9)) ; visual aids to help with 1-9 keys
552775bd
RS
2473 (put-text-property (match-beginning 0)
2474 (1+ (match-beginning 0))
2475 'face 'info-menu-5))
f7902bdc
DL
2476 (add-text-properties (match-beginning 1) (match-end 1)
2477 '(face info-xref
2478 mouse-face highlight
2479 help-echo "mouse-2: go to this node")))))
973a3104 2480 (Info-fontify-menu-headers)
552775bd 2481 (set-buffer-modified-p nil))))
c5fe2ff1
RS
2482\f
2483
2484;; When an Info buffer is killed, make sure the associated tags buffer
2485;; is killed too.
2486(defun Info-kill-buffer ()
2487 (and (eq major-mode 'Info-mode)
2488 Info-tag-table-buffer
2489 (kill-buffer Info-tag-table-buffer)))
2490
2491(add-hook 'kill-buffer-hook 'Info-kill-buffer)
3cb6768f
EL
2492
2493;;; Speedbar support:
2494;; These functions permit speedbar to display the "tags" in the
2495;; current info node.
96ee3f29 2496(eval-when-compile (require 'speedbar))
3cb6768f 2497
96ee3f29
KH
2498(defvar Info-speedbar-key-map nil
2499 "Keymap used when in the info display mode.")
3cb6768f 2500
96ee3f29
KH
2501(defun Info-install-speedbar-variables ()
2502 "Install those variables used by speedbar to enhance Info."
2503 (if Info-speedbar-key-map
2504 nil
2505 (setq Info-speedbar-key-map (speedbar-make-specialized-keymap))
2506
2507 ;; Basic tree features
2508 (define-key Info-speedbar-key-map "e" 'speedbar-edit-line)
2509 (define-key Info-speedbar-key-map "\C-m" 'speedbar-edit-line)
2510 (define-key Info-speedbar-key-map "+" 'speedbar-expand-line)
2511 (define-key Info-speedbar-key-map "-" 'speedbar-contract-line)
2512 )
2513
2514 (speedbar-add-expansion-list '("Info" Info-speedbar-menu-items
2515 Info-speedbar-key-map
2516 Info-speedbar-hierarchy-buttons)))
3cb6768f
EL
2517
2518(defvar Info-speedbar-menu-items
96ee3f29
KH
2519 '(["Browse Node" speedbar-edit-line t]
2520 ["Expand Node" speedbar-expand-line
2521 (save-excursion (beginning-of-line)
2522 (looking-at "[0-9]+: *.\\+. "))]
2523 ["Contract Node" speedbar-contract-line
2524 (save-excursion (beginning-of-line)
2525 (looking-at "[0-9]+: *.-. "))]
2526 )
3cb6768f
EL
2527 "Additional menu-items to add to speedbar frame.")
2528
96ee3f29
KH
2529;; Make sure our special speedbar major mode is loaded
2530(if (featurep 'speedbar)
2531 (Info-install-speedbar-variables)
2532 (add-hook 'speedbar-load-hook 'Info-install-speedbar-variables))
2533
2534;;; Info hierarchy display method
2535;;;###autoload
2536(defun Info-speedbar-browser ()
2537 "Initialize speedbar to display an info node browser.
2538This will add a speedbar major display mode."
2539 (interactive)
2540 (require 'speedbar)
2541 ;; Make sure that speedbar is active
2542 (speedbar-frame-mode 1)
2543 ;; Now, throw us into Info mode on speedbar.
2544 (speedbar-change-initial-expansion-list "Info")
2545 )
2546
7210b6f5
DL
2547(eval-when-compile (defvar speedbar-attached-frame))
2548
96ee3f29
KH
2549(defun Info-speedbar-hierarchy-buttons (directory depth &optional node)
2550 "Display an Info directory hierarchy in speedbar.
2551DIRECTORY is the current directory in the attached frame.
2552DEPTH is the current indentation depth.
2553NODE is an optional argument that is used to represent the
2554specific node to expand."
2555 (if (and (not node)
2556 (save-excursion (goto-char (point-min))
1bcedb3b
RS
2557 (let ((case-fold-search t))
2558 (looking-at "Info Nodes:"))))
96ee3f29
KH
2559 ;; Update our "current node" maybe?
2560 nil
2561 ;; We cannot use the generic list code, that depends on all leaves
2562 ;; being known at creation time.
2563 (if (not node)
2564 (speedbar-with-writable (insert "Info Nodes:\n")))
5fdc7997
EL
2565 (let ((completions nil)
2566 (cf (selected-frame)))
2567 (select-frame speedbar-attached-frame)
2568 (save-window-excursion
2569 (setq completions
2570 (Info-speedbar-fetch-file-nodes (or node '"(dir)top"))))
2571 (select-frame cf)
96ee3f29
KH
2572 (if completions
2573 (speedbar-with-writable
2574 (while completions
2575 (speedbar-make-tag-line 'bracket ?+ 'Info-speedbar-expand-node
2576 (cdr (car completions))
2577 (car (car completions))
2578 'Info-speedbar-goto-node
2579 (cdr (car completions))
2580 'info-xref depth)
2581 (setq completions (cdr completions)))
2582 t)
2583 nil))))
399c88ad 2584
96ee3f29 2585(defun Info-speedbar-goto-node (text node indent)
d1268e52 2586 "When user clicks on TEXT, go to an info NODE.
96ee3f29 2587The INDENT level is ignored."
760d5cb3
GM
2588 (select-frame speedbar-attached-frame)
2589 (let* ((buff (or (get-buffer "*info*")
2590 (progn (info) (get-buffer "*info*"))))
2591 (bwin (get-buffer-window buff 0)))
2592 (if bwin
2593 (progn
2594 (select-window bwin)
2595 (raise-frame (window-frame bwin)))
2596 (if speedbar-power-click
2597 (let ((pop-up-frames t)) (select-window (display-buffer buff)))
2598 (select-frame speedbar-attached-frame)
2599 (switch-to-buffer buff)))
2600 (let ((junk (string-match "^(\\([^)]+\\))\\([^.]+\\)$" node))
2601 (file (match-string 1 node))
2602 (node (match-string 2 node)))
2603 (Info-find-node file node)
2604 ;; If we do a find-node, and we were in info mode, restore
2605 ;; the old default method. Once we are in info mode, it makes
2606 ;; sense to return to whatever method the user was using before.
2607 (if (string= speedbar-initial-expansion-list-name "Info")
2608 (speedbar-change-initial-expansion-list
2609 speedbar-previously-used-expansion-list-name)))))
96ee3f29
KH
2610
2611(defun Info-speedbar-expand-node (text token indent)
2612 "Expand the node the user clicked on.
2613TEXT is the text of the button we clicked on, a + or - item.
2614TOKEN is data related to this node (NAME . FILE).
2615INDENT is the current indentation depth."
2616 (cond ((string-match "+" text) ;we have to expand this file
2617 (speedbar-change-expand-button-char ?-)
2618 (if (speedbar-with-writable
760d5cb3
GM
2619 (save-excursion
2620 (end-of-line) (forward-char 1)
2621 (Info-speedbar-hierarchy-buttons nil (1+ indent) token)))
96ee3f29
KH
2622 (speedbar-change-expand-button-char ?-)
2623 (speedbar-change-expand-button-char ??)))
2624 ((string-match "-" text) ;we have to contract this node
2625 (speedbar-change-expand-button-char ?+)
2626 (speedbar-delete-subblock indent))
2627 (t (error "Ooops... not sure what to do")))
2628 (speedbar-center-buffer-smartly))
2629
2630(defun Info-speedbar-fetch-file-nodes (nodespec)
2631 "Fetch the subnodes from the info NODESPEC.
2632NODESPEC is a string of the form: (file)node.
2633Optional THISFILE represends the filename of"
2634 (save-excursion
2635 ;; Set up a buffer we can use to fake-out Info.
2636 (set-buffer (get-buffer-create "*info-browse-tmp*"))
2637 (if (not (equal major-mode 'Info-mode))
2638 (Info-mode))
2639 ;; Get the node into this buffer
2640 (let ((junk (string-match "^(\\([^)]+\\))\\([^.]+\\)$" nodespec))
2641 (file (match-string 1 nodespec))
2642 (node (match-string 2 nodespec)))
2643 (Info-find-node file node))
2644 ;; Scan the created buffer
2645 (goto-char (point-min))
2646 (let ((completions nil)
1bcedb3b 2647 (case-fold-search t)
96ee3f29
KH
2648 (thisfile (progn (string-match "^(\\([^)]+\\))" nodespec)
2649 (match-string 1 nodespec))))
2650 ;; Always skip the first one...
2651 (re-search-forward "\n\\* \\([^:\t\n]*\\):" nil t)
2652 (while (re-search-forward "\n\\* \\([^:\t\n]*\\):" nil t)
2653 (let ((name (match-string 1)))
2654 (if (looking-at " *\\(([^)]+)[^.\n]+\\)\\.")
2655 (setq name (cons name (match-string 1)))
2656 (if (looking-at " *\\(([^)]+)\\)\\.")
2657 (setq name (cons name (concat (match-string 1) "Top")))
2658 (if (looking-at " \\([^.]+\\).")
2659 (setq name
2660 (cons name (concat "(" thisfile ")" (match-string 1))))
2661 (setq name (cons name (concat "(" thisfile ")" name))))))
2662 (setq completions (cons name completions))))
2663 (nreverse completions))))
2664
2665;;; Info mode node listing
3cb6768f
EL
2666(defun Info-speedbar-buttons (buffer)
2667 "Create a speedbar display to help navigation in an Info file.
2668BUFFER is the buffer speedbar is requesting buttons for."
96ee3f29 2669 (if (save-excursion (goto-char (point-min))
1bcedb3b
RS
2670 (let ((case-fold-search t))
2671 (not (looking-at "Info Nodes:"))))
96ee3f29
KH
2672 (erase-buffer))
2673 (Info-speedbar-hierarchy-buttons nil 0)
2674 )
49116ac0 2675
48e5b471
DL
2676(dolist (mess '("^Node has no Previous$"
2677 "^No menu in this node$"
2678 "^Node has no Next$"
399c88ad
SS
2679 "^No cross-references in this node^"
2680 search-failed
48e5b471
DL
2681 "^No \".*\" in index$"))
2682 (add-to-list 'debug-ignored-errors mess))
2683
49116ac0
JB
2684(provide 'info)
2685
1a06eabd 2686;;; info.el ends here