*** empty log message ***
[bpt/emacs.git] / lisp / info.el
CommitLineData
55535639 1;;; info.el --- info package for Emacs
e5167999 2
f6b2b74d 3;; Copyright (C) 1985,86,92,93,94,95,96,97,98,99,2000,01,02,03,2004
5b3ebb44 4;; Free Software 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.
921e5fe6
TTN
29;; In particular we make these assumptions:
30;; - a menu item MAY contain colons but not colon-space ": "
31;; - a menu item ending with ": " (but not ":: ") is an index entry
32;; - a node name MAY NOT contain a colon
505b68d5
TTN
33;; This distinction is to support indexing of computer programming
34;; language terms that may contain ":" but not ": ".
e5167999
ER
35
36;;; Code:
37
bc6262c8
DL
38(eval-when-compile (require 'jka-compr))
39
ded3e3d8
RS
40(defgroup info nil
41 "Info subsystem"
42 :group 'help
43 :group 'docs)
44
45
a384cab3 46(defvar Info-history nil
8a7757f6 47 "Stack of info nodes user has visited.
a384cab3
JB
48Each element of list is a list (FILENAME NODENAME BUFFERPOS).")
49
424b9d3e
JL
50(defvar Info-history-forward nil
51 "Stack of info nodes user has visited with `Info-history-back' command.
52Each element of list is a list (FILENAME NODENAME BUFFERPOS).")
53
8a7757f6
JL
54(defvar Info-history-list nil
55 "List of all info nodes user has visited.
56Each element of list is a list (FILENAME NODENAME).")
57
ded3e3d8 58(defcustom Info-enable-edit nil
d5913bf6 59 "*Non-nil means the \\<Info-mode-map>\\[Info-edit] command in Info can edit the current node.
ada0a60d
RS
60This is convenient if you want to write info files by hand.
61However, we recommend that you not do this.
62It is better to write a Texinfo file and generate the Info file from that,
ded3e3d8
RS
63because that gives you a printed manual as well."
64 :type 'boolean
65 :group 'info)
a384cab3 66
67f445d7 67(defvar Info-enable-active-nodes nil
a384cab3
JB
68 "Non-nil allows Info to execute Lisp code associated with nodes.
69The Lisp code is executed when the node is selected.")
67f445d7 70(put 'Info-enable-active-nodes 'risky-local-variable t)
a384cab3 71
93480d70 72(defface info-node
508bce68
SM
73 '((((class color) (background light)) :foreground "brown" :weight bold :slant italic)
74 (((class color) (background dark)) :foreground "white" :weight bold :slant italic)
75 (t :weight bold :slant italic))
93480d70
RS
76 "Face for Info node names."
77 :group 'info)
78
79(defface info-menu-5
508bce68
SM
80 '((((class color)) :foreground "red1")
81 (t :underline t))
ccfb7415 82 "Face for every third `*' in an Info menu."
93480d70
RS
83 :group 'info)
84
85(defface info-xref
cc3f6c32
RS
86 '((((class color) (background light)) :foreground "blue" :underline t)
87 (((class color) (background dark)) :foreground "cyan" :underline t)
508bce68 88 (t :underline t))
93480d70
RS
89 "Face for Info cross-references."
90 :group 'info)
91
8a7757f6 92(defface info-xref-visited
6b3600ff 93 '((default :inherit info-xref)
508bce68 94 (((class color) (background light)) :foreground "magenta4")
836bb478 95 (((class color) (background dark)) :foreground "magenta3")) ;"violet"?
8a7757f6
JL
96 "Face for visited Info cross-references."
97 :group 'info)
98
99(defcustom Info-fontify-visited-nodes t
100 "*Non-nil means to fontify visited nodes in a different face."
bf247b6e 101 :version "22.1"
8a7757f6
JL
102 :type 'boolean
103 :group 'info)
104
88ef7f62 105(defcustom Info-fontify-maximum-menu-size 100000
ccfb7415 106 "*Maximum size of menu to fontify if `font-lock-mode' is non-nil."
ded3e3d8
RS
107 :type 'integer
108 :group 'info)
bdf62a4d 109
50ac70af 110(defcustom Info-use-header-line t
c3717746 111 "*Non-nil means to put the beginning-of-node links in an Emacs header-line.
50ac70af
MB
112A header-line does not scroll with the rest of the buffer."
113 :type 'boolean
114 :group 'info)
115
116(defface info-header-xref
508bce68 117 '((t :inherit info-xref))
50ac70af
MB
118 "Face for Info cross-references in a node header."
119 :group 'info)
120
121(defface info-header-node
508bce68 122 '((t :inherit info-node))
50ac70af
MB
123 "Face for Info nodes in a node header."
124 :group 'info)
125
7d3766a8 126(defvar Info-directory-list nil
a384cab3 127 "List of directories to search for Info documentation files.
fc4d14c5 128If nil, meaning not yet initialized, Info uses the environment
44c327f9 129variable INFOPATH to initialize it, or `Info-default-directory-list'
dde7f979
SM
130if there is no INFOPATH variable in the environment, or the
131concatenation of the two if INFOPATH ends with a colon.
778911b9
EZ
132
133When `Info-directory-list' is initialized from the value of
4dddb0b7
EZ
134`Info-default-directory-list', and Emacs is installed in one of the
135standard directories, the directory of Info files that come with Emacs
136is put last (so that local Info files override standard ones).
137
138When `Info-directory-list' is initialized from the value of
139`Info-default-directory-list', and Emacs is not installed in one
140of the standard directories, the first element of the resulting
778911b9
EZ
141list is the directory where Emacs installs the Info files that
142come with it. This is so that Emacs's own manual, which suits the
4dddb0b7
EZ
143version of Emacs you are using, will always be found first. This
144is useful when you install an experimental version of Emacs without
145removing the standard installation.
146
147If you want to override the order of directories in
148`Info-default-directory-list', set INFOPATH in the environment.
5da0f945
RS
149
150If you run the Emacs executable from the `src' directory in the Emacs
778911b9
EZ
151source tree, and INFOPATH is not defined, the `info' directory in the
152source tree is used as the first element of `Info-directory-list', in
153place of the installation Info directory. This is useful when you run
154a version of Emacs without installing it.")
a384cab3 155
ded3e3d8 156(defcustom Info-additional-directory-list nil
bdf62a4d 157 "List of additional directories to search for Info documentation files.
e9b1d996 158These directories are searched after those in `Info-directory-list'."
ded3e3d8
RS
159 :type '(repeat directory)
160 :group 'info)
bdf62a4d 161
610bc96b 162(defcustom Info-scroll-prefer-subnodes nil
7a53d8c8
EZ
163 "*If non-nil, \\<Info-mode-map>\\[Info-scroll-up] in a menu visits subnodes.
164If this is non-nil, and you scroll far enough in a node that its menu
165appears on the screen, the next \\<Info-mode-map>\\[Info-scroll-up]
166moves to a subnode indicated by the following menu item. This means
167that you visit a subnode before getting to the end of the menu.
168
169Setting this option to nil results in behavior similar to the stand-alone
170Info reader program, which visits the first subnode from the menu only
171when you hit the end of the current node."
bf247b6e 172 :version "22.1"
7a53d8c8
EZ
173 :type 'boolean
174 :group 'info)
175
a9efebd0 176(defcustom Info-hide-note-references t
5f812a3c 177 "*If non-nil, hide the tag and section reference in *note and * menu items.
8a7757f6
JL
178If value is non-nil but not `hide', also replaces the \"*note\" with \"see\".
179If value is non-nil but not t or `hide', the reference section is still shown."
bf247b6e 180 :version "22.1"
8a7757f6 181 :type '(choice (const :tag "No hiding" nil)
0452a274 182 (const :tag "Replace tag and hide reference" t)
8a7757f6
JL
183 (const :tag "Hide tag and reference" hide)
184 (other :tag "Only replace tag" tag))
a9efebd0
KS
185 :group 'info)
186
bb1e1730
KS
187(defcustom Info-refill-paragraphs nil
188 "*If non-nil, attempt to refill paragraphs with hidden references.
189This refilling may accidentally remove explicit line breaks in the info
190file, so be prepared for a few surprises if you enable this feature."
bf247b6e 191 :version "22.1"
bb1e1730
KS
192 :type 'boolean
193 :group 'info)
194
180ee7c7 195(defcustom Info-search-whitespace-regexp "\\s-+"
8a7757f6
JL
196 "*If non-nil, regular expression to match a sequence of whitespace chars.
197This applies to Info search for regular expressions.
198You might want to use something like \"[ \\t\\r\\n]+\" instead.
199In the Customization buffer, that is `[' followed by a space,
200a tab, a carriage return (control-M), a newline, and `]+'."
201 :type 'regexp
202 :group 'info)
203
98e77be5
JL
204(defcustom Info-isearch-search t
205 "*If non-nil, isearch invoked in Info mode uses `Info-search' function.
206This allows isearch to search through multiple nodes.
207When isearch fails, it wraps and restarts the search from the
208top/final node depending on search direction."
209 :version "22.1"
210 :type 'boolean
211 :group 'info)
212
d457219d
JL
213(defvar Info-isearch-initial-node nil)
214
dde7f979
SM
215(defcustom Info-mode-hook
216 ;; Try to obey obsolete Info-fontify settings.
217 (unless (and (boundp 'Info-fontify) (null Info-fontify))
218 '(turn-on-font-lock))
8a7757f6
JL
219 "Hooks run when `Info-mode' is called."
220 :type 'hook
221 :group 'info)
222
223(defcustom Info-selection-hook nil
224 "Hooks run when `Info-select-node' is called."
544e5562
CW
225 :type 'hook
226 :group 'info)
227
8a7757f6
JL
228(defvar Info-edit-mode-hook nil
229 "Hooks run when `Info-edit-mode' is called.")
230
a384cab3 231(defvar Info-current-file nil
d6be34f3
RS
232 "Info file that Info is now looking at, or nil.
233This is the name that was specified in Info, not the actual file name.
4fceda3c
SM
234It doesn't contain directory names or file name extensions added by Info.
235Can also be t when using `Info-on-current-buffer'.")
a384cab3
JB
236
237(defvar Info-current-subfile nil
fdf4b680
DL
238 "Info subfile that is actually in the *info* buffer now.
239nil if current info file is not split into subfiles.")
a384cab3
JB
240
241(defvar Info-current-node nil
242 "Name of node that Info is now looking at, or nil.")
243
c5fe2ff1 244(defvar Info-tag-table-marker nil
a384cab3
JB
245 "Marker pointing at beginning of current Info file's tag table.
246Marker points nowhere if file has no tag table.")
247
c5fe2ff1
RS
248(defvar Info-tag-table-buffer nil
249 "Buffer used for indirect tag tables.")
250
552775bd
RS
251(defvar Info-current-file-completions nil
252 "Cached completion list for current Info file.")
253
1143a6b0 254(defvar Info-index-alternatives nil
fdf4b680 255 "List of possible matches for last `Info-index' command.")
1143a6b0 256
836bb478
JL
257(defvar Info-point-loc nil
258 "Point location within a selected node.
259If string, the point is moved to the proper occurrence of the
260name of the followed cross reference within a selected node.
261If number, the point is moved to the corresponding line.")
8a7757f6 262
552775bd
RS
263(defvar Info-standalone nil
264 "Non-nil if Emacs was started solely as an Info browser.")
7d3766a8 265\f
bd0c9168 266(defvar Info-suffix-list
3c19e6c1
RS
267 ;; The MS-DOS list should work both when long file names are
268 ;; supported (Windows 9X), and when only 8+3 file names are available.
bd0c9168
RS
269 (if (eq system-type 'ms-dos)
270 '( (".gz" . "gunzip")
271 (".z" . "gunzip")
544e210f 272 (".bz2" . ("bzip2" "-dc"))
3c19e6c1
RS
273 (".inz" . "gunzip")
274 (".igz" . "gunzip")
275 (".info.Z" . "gunzip")
276 (".info.gz" . "gunzip")
277 ("-info.Z" . "gunzip")
278 ("-info.gz" . "gunzip")
279 ("/index.gz". "gunzip")
280 ("/index.z" . "gunzip")
9b842cab 281 (".inf" . nil)
3c19e6c1
RS
282 (".info" . nil)
283 ("-info" . nil)
284 ("/index" . nil)
bd0c9168 285 ("" . nil))
a70dc410
RS
286 '( (".info.Z". "uncompress")
287 (".info.Y". "unyabba")
288 (".info.gz". "gunzip")
289 (".info.z". "gunzip")
544e210f 290 (".info.bz2" . ("bzip2" "-dc"))
a70dc410
RS
291 (".info". nil)
292 ("-info.Z". "uncompress")
293 ("-info.Y". "unyabba")
294 ("-info.gz". "gunzip")
544e210f 295 ("-info.bz2" . ("bzip2" "-dc"))
a70dc410
RS
296 ("-info.z". "gunzip")
297 ("-info". nil)
298 ("/index.Z". "uncompress")
299 ("/index.Y". "unyabba")
300 ("/index.gz". "gunzip")
301 ("/index.z". "gunzip")
544e210f 302 ("/index.bz2". ("bzip2" "-dc"))
a70dc410
RS
303 ("/index". nil)
304 (".Z". "uncompress")
305 (".Y". "unyabba")
306 (".gz". "gunzip")
307 (".z". "gunzip")
544e210f 308 (".bz2" . ("bzip2" "-dc"))
a70dc410 309 ("". nil)))
1143a6b0
ER
310 "List of file name suffixes and associated decoding commands.
311Each entry should be (SUFFIX . STRING); the file is given to
544e210f
KH
312the command as standard input.
313
314STRING may be a list of strings. In that case, the first element is
315the command name, and the rest are arguments to that command.
316
317If STRING is nil, no decoding is done.
97f99202
KH
318Because the SUFFIXes are tried in order, the empty string should
319be last in the list.")
1143a6b0 320
c8e9dd54 321;; Concatenate SUFFIX onto FILENAME. SUFFIX should start with a dot.
e0c1e774
EZ
322;; First, on MS-DOS with no long file names support, delete some of
323;; the extension in FILENAME to make room.
324(defun info-insert-file-contents-1 (filename suffix lfn)
325 (if lfn ; long file names are supported
bd0c9168
RS
326 (concat filename suffix)
327 (let* ((sans-exts (file-name-sans-extension filename))
c8e9dd54
RS
328 ;; How long is the extension in FILENAME (not counting the dot).
329 (ext-len (max 0 (- (length filename) (length sans-exts) 1)))
330 ext-left)
9b842cab 331 ;; SUFFIX starts with a dot. If FILENAME already has one,
1583fe94 332 ;; get rid of the one in SUFFIX (unless suffix is empty).
c8e9dd54 333 (or (and (<= ext-len 0)
9b842cab 334 (not (eq (aref filename (1- (length filename))) ?.)))
1583fe94 335 (= (length suffix) 0)
9b842cab 336 (setq suffix (substring suffix 1)))
c8e9dd54
RS
337 ;; How many chars of that extension should we keep?
338 (setq ext-left (min ext-len (max 0 (- 3 (length suffix)))))
bd0c9168
RS
339 ;; Get rid of the rest of the extension, and add SUFFIX.
340 (concat (substring filename 0 (- (length filename)
341 (- ext-len ext-left)))
342 suffix))))
343
9c7924d5
RS
344(defun info-file-exists-p (filename)
345 (and (file-exists-p filename)
346 (not (file-directory-p filename))))
347
1143a6b0
ER
348(defun info-insert-file-contents (filename &optional visit)
349 "Insert the contents of an info file in the current buffer.
350Do the right thing if the file has been compressed or zipped."
e0c1e774 351 (let* ((tail Info-suffix-list)
ab5a535c
KS
352 (lfn (if (fboundp 'msdos-long-file-names)
353 (msdos-long-file-names)
354 t))
e0c1e774
EZ
355 (check-short (and (fboundp 'msdos-long-file-names)
356 lfn))
357 fullname decoder done)
b8fa4e87 358 (if (info-file-exists-p filename)
bd0c9168
RS
359 ;; FILENAME exists--see if that name contains a suffix.
360 ;; If so, set DECODE accordingly.
97f99202
KH
361 (progn
362 (while (and tail
363 (not (string-match
364 (concat (regexp-quote (car (car tail))) "$")
365 filename)))
366 (setq tail (cdr tail)))
367 (setq fullname filename
368 decoder (cdr (car tail))))
bd0c9168 369 ;; Try adding suffixes to FILENAME and see if we can find something.
e0c1e774
EZ
370 (while (and tail (not done))
371 (setq fullname (info-insert-file-contents-1 filename
372 (car (car tail)) lfn))
373 (if (info-file-exists-p fullname)
374 (setq done t
375 ;; If we found a file with a suffix, set DECODER
376 ;; according to the suffix.
377 decoder (cdr (car tail)))
378 ;; When the MS-DOS port runs on Windows, we need to check
379 ;; the short variant of a long file name as well.
380 (when check-short
381 (setq fullname (info-insert-file-contents-1 filename
382 (car (car tail)) nil))
383 (if (info-file-exists-p fullname)
384 (setq done t
385 decoder (cdr (car tail))))))
97f99202 386 (setq tail (cdr tail)))
97f99202 387 (or tail
bd0c9168 388 (error "Can't find %s or any compressed version of it" filename)))
e175bda2
RS
389 ;; check for conflict with jka-compr
390 (if (and (featurep 'jka-compr)
391 (jka-compr-installed-p)
392 (jka-compr-get-compression-info fullname))
393 (setq decoder nil))
97f99202 394 (if decoder
3c19e6c1
RS
395 (progn
396 (insert-file-contents-literally fullname visit)
397 (let ((buffer-read-only nil)
398 (coding-system-for-write 'no-conversion)
399 (default-directory (or (file-name-directory fullname)
400 default-directory)))
544e210f
KH
401 (or (consp decoder)
402 (setq decoder (list decoder)))
403 (apply 'call-process-region (point-min) (point-max)
404 (car decoder) t t nil (cdr decoder))))
3c19e6c1 405 (insert-file-contents fullname visit))))
7d3766a8 406\f
3a59b662
SM
407(defun Info-default-dirs ()
408 (let ((source (expand-file-name "info/" source-directory))
409 (sibling (if installation-directory
410 (expand-file-name "info/" installation-directory)
411 (if invocation-directory
412 (let ((infodir (expand-file-name
413 "../info/"
414 invocation-directory)))
415 (if (file-exists-p infodir)
416 infodir
417 (setq infodir (expand-file-name
418 "../../../info/"
419 invocation-directory))
420 (and (file-exists-p infodir)
421 infodir))))))
422 alternative)
423 (setq alternative
424 (if (and sibling (file-exists-p sibling))
425 ;; Uninstalled, Emacs builddir != srcdir.
426 sibling
427 ;; Uninstalled, builddir == srcdir
428 source))
429 (if (or (member alternative Info-default-directory-list)
430 ;; On DOS/NT, we use movable executables always,
431 ;; and we must always find the Info dir at run time.
432 (if (memq system-type '(ms-dos windows-nt))
433 nil
434 ;; Use invocation-directory for Info
435 ;; only if we used it for exec-directory also.
436 (not (string= exec-directory
437 (expand-file-name "lib-src/"
438 installation-directory))))
439 (not (file-exists-p alternative)))
440 Info-default-directory-list
441 ;; `alternative' contains the Info files that came with this
442 ;; version, so we should look there first. `Info-insert-dir'
443 ;; currently expects to find `alternative' first on the list.
444 (cons alternative
a025fccf
SM
445 ;; Don't drop the last part, it might contain non-Emacs stuff.
446 ;; (reverse (cdr (reverse
447 Info-default-directory-list)))) ;; )))
3a59b662 448
7d3766a8 449(defun info-initialize ()
fdf4b680 450 "Initialize `Info-directory-list', if that hasn't been done yet."
7d3766a8 451 (unless Info-directory-list
3a59b662 452 (let ((path (getenv "INFOPATH")))
7d3766a8 453 (setq Info-directory-list
78e7e8a0
MB
454 (prune-directory-list
455 (if path
3a59b662
SM
456 (if (string-match ":\\'" path)
457 (append (split-string (substring path 0 -1)
458 (regexp-quote path-separator))
459 (Info-default-dirs))
460 (split-string path (regexp-quote path-separator)))
461 (Info-default-dirs)))))))
1143a6b0 462
a6e4564e
RS
463;;;###autoload
464(defun info-other-window (&optional file)
465 "Like `info' but show the Info buffer in another window."
466 (interactive (if current-prefix-arg
467 (list (read-file-name "Info file name: " nil nil t))))
836bb478 468 (let (same-window-buffer-names same-window-regexps)
a6e4564e 469 (info file)))
399c88ad 470
836bb478 471;;;###autoload (add-hook 'same-window-regexps "\\*info\\*\\(\\|<[0-9]+>\\)")
211d6309 472
cc3f6c32 473;;;###autoload (put 'info 'info-file "emacs")
a384cab3 474;;;###autoload
836bb478 475(defun info (&optional file buffer)
a384cab3
JB
476 "Enter Info, the documentation browser.
477Optional argument FILE specifies the file to examine;
478the default is the top-level directory of Info.
83d5c772
DL
479Called from a program, FILE may specify an Info node of the form
480`(FILENAME)NODENAME'.
836bb478
JL
481Optional argument BUFFER specifies the Info buffer name;
482the default buffer name is *info*. If BUFFER exists,
483just switch to BUFFER. Otherwise, create a new buffer
484with the top-level Info directory.
a384cab3 485
836bb478
JL
486In interactive use, a non-numeric prefix argument directs
487this command to read a file name from the minibuffer.
bc224645
JL
488A numeric prefix argument selects an Info buffer with the prefix number
489appended to the Info buffer name.
e341dab2
RS
490
491The search path for Info files is in the variable `Info-directory-list'.
399c88ad 492The top-level Info directory is made by combining all the files named `dir'
e341dab2 493in all the directories in that path."
836bb478
JL
494 (interactive (list
495 (if (and current-prefix-arg (not (numberp current-prefix-arg)))
496 (read-file-name "Info file name: " nil nil t))
497 (if (numberp current-prefix-arg)
498 (format "*info*<%s>" current-prefix-arg))))
499 (pop-to-buffer (or buffer "*info*"))
500 (if (and buffer (not (eq major-mode 'Info-mode)))
501 (Info-mode))
a384cab3 502 (if file
60705054
SM
503 ;; If argument already contains parentheses, don't add another set
504 ;; since the argument will then be parsed improperly. This also
505 ;; has the added benefit of allowing node names to be included
506 ;; following the parenthesized filename.
507 (if (and (stringp file) (string-match "(.*)" file))
508 (Info-goto-node file)
509 (Info-goto-node (concat "(" file ")")))
510 (if (zerop (buffer-size))
89b064a9 511 (Info-directory))))
a384cab3 512
c3717746
RS
513;;;###autoload
514(defun info-emacs-manual ()
3a8803c0 515 "Display the Emacs manual in Info mode."
c3717746
RS
516 (interactive)
517 (info "emacs"))
518
552775bd
RS
519;;;###autoload
520(defun info-standalone ()
521 "Run Emacs as a standalone Info reader.
522Usage: emacs -f info-standalone [filename]
523In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
524 (setq Info-standalone t)
525 (if (and command-line-args-left
526 (not (string-match "^-" (car command-line-args-left))))
527 (condition-case err
528 (progn
529 (info (car command-line-args-left))
530 (setq command-line-args-left (cdr command-line-args-left)))
531 (error (send-string-to-terminal
532 (format "%s\n" (if (eq (car-safe err) 'error)
533 (nth 1 err) err)))
534 (save-buffers-kill-emacs)))
535 (info)))
7d3766a8 536\f
983dfbf8 537;; See if the accessible portion of the buffer begins with a node
11638562
EZ
538;; delimiter, and the node header line which follows matches REGEXP.
539;; Typically, this test will be followed by a loop that examines the
540;; rest of the buffer with (search-forward "\n\^_"), and it's a pity
541;; to have the overhead of this special test inside the loop.
542
543;; This function changes match-data, but supposedly the caller might
544;; want to use the results of re-search-backward.
545
546;; The return value is the value of point at the beginning of matching
983dfbf8 547;; REGEXP, if the function succeeds, nil otherwise.
11638562 548(defun Info-node-at-bob-matching (regexp)
760d5cb3
GM
549 (and (bobp) ; are we at beginning of buffer?
550 (looking-at "\^_") ; does it begin with node delimiter?
11638562
EZ
551 (let (beg)
552 (forward-line 1)
553 (setq beg (point))
760d5cb3 554 (forward-line 1) ; does the line after delimiter match REGEXP?
11638562
EZ
555 (re-search-backward regexp beg t))))
556
8a7757f6
JL
557(defun Info-find-file (filename &optional noerror)
558 "Return expanded FILENAME, or t, if FILENAME is \"dir\".
559Optional second argument NOERROR, if t, means if file is not found
560just return nil (no error)."
a384cab3
JB
561 ;; Convert filename to lower case if not found as specified.
562 ;; Expand it.
4fceda3c 563 (if (stringp filename)
a384cab3 564 (let (temp temp-downcase found)
37a3ff5b 565 (setq filename (substitute-in-file-name filename))
89b064a9 566 (cond
ab2f22ad
JH
567 ((string= (downcase filename) "dir")
568 (setq found t))
569 ((string= filename "apropos")
570 (setq found 'apropos))
89b064a9
JL
571 ((string= filename "history")
572 (setq found 'history))
573 ((string= filename "toc")
574 (setq found 'toc))
ab2f22ad
JH
575 (t
576 (let ((dirs (if (string-match "^\\./" filename)
37a3ff5b
RS
577 ;; If specified name starts with `./'
578 ;; then just try current directory.
579 '("./")
580 (if (file-name-absolute-p filename)
581 ;; No point in searching for an
582 ;; absolute file name
583 '(nil)
584 (if Info-additional-directory-list
585 (append Info-directory-list
586 Info-additional-directory-list)
587 Info-directory-list)))))
588 ;; Search the directory list for file FILENAME.
589 (while (and dirs (not found))
590 (setq temp (expand-file-name filename (car dirs)))
591 (setq temp-downcase
592 (expand-file-name (downcase filename) (car dirs)))
593 ;; Try several variants of specified name.
e0c1e774 594 (let ((suffix-list Info-suffix-list)
ab5a535c
KS
595 (lfn (if (fboundp 'msdos-long-file-names)
596 (msdos-long-file-names)
597 t)))
37a3ff5b
RS
598 (while (and suffix-list (not found))
599 (cond ((info-file-exists-p
600 (info-insert-file-contents-1
e0c1e774 601 temp (car (car suffix-list)) lfn))
37a3ff5b
RS
602 (setq found temp))
603 ((info-file-exists-p
604 (info-insert-file-contents-1
e0c1e774
EZ
605 temp-downcase (car (car suffix-list)) lfn))
606 (setq found temp-downcase))
607 ((and (fboundp 'msdos-long-file-names)
608 lfn
609 (info-file-exists-p
610 (info-insert-file-contents-1
611 temp (car (car suffix-list)) nil)))
612 (setq found temp)))
37a3ff5b 613 (setq suffix-list (cdr suffix-list))))
ab2f22ad 614 (setq dirs (cdr dirs))))))
37a3ff5b
RS
615 (if found
616 (setq filename found)
8a7757f6
JL
617 (if noerror
618 (setq filename nil)
619 (error "Info file %s does not exist" filename)))
620 filename)))
621
622(defun Info-find-node (filename nodename &optional no-going-back)
623 "Go to an info node specified as separate FILENAME and NODENAME.
624NO-GOING-BACK is non-nil if recovering from an error in this function;
625it says do not attempt further (recursive) error recovery."
626 (info-initialize)
627 (setq filename (Info-find-file filename))
37a3ff5b
RS
628 ;; Record the node we are leaving.
629 (if (and Info-current-file (not no-going-back))
630 (setq Info-history
631 (cons (list Info-current-file Info-current-node (point))
632 Info-history)))
a384cab3 633 ;; Go into info buffer.
c5fe2ff1 634 (or (eq major-mode 'Info-mode) (pop-to-buffer "*info*"))
4fceda3c
SM
635 (Info-find-node-2 filename nodename no-going-back))
636
637(defun Info-on-current-buffer (&optional nodename)
638 "Use the `Info-mode' to browse the current info buffer.
639If a prefix arg is provided, it queries for the NODENAME which
07e91aa3 640else defaults to \"Top\"."
4fceda3c
SM
641 (interactive
642 (list (if current-prefix-arg
643 (completing-read "Node name: " (Info-build-node-completions)
07e91aa3
SM
644 nil t "Top"))))
645 (unless nodename (setq nodename "Top"))
75296efc 646 (info-initialize)
4fceda3c
SM
647 (Info-mode)
648 (set (make-local-variable 'Info-current-file) t)
649 (Info-find-node-2 nil nodename))
650
f584b4c7
RS
651;; It's perhaps a bit nasty to kill the *info* buffer to force a re-read,
652;; but at least it keeps this routine (which is only for the benefit of
653;; makeinfo-buffer) out of the way of normal operations.
654;;
655(defun Info-revert-find-node (filename nodename)
656 "Go to an info node FILENAME and NODENAME, re-reading disk contents.
657When *info* is already displaying FILENAME and NODENAME, the window position
658is preserved, if possible."
659 (pop-to-buffer "*info*")
660 (let ((old-filename Info-current-file)
661 (old-nodename Info-current-node)
662 (pcolumn (current-column))
663 (pline (count-lines (point-min) (line-beginning-position)))
664 (wline (count-lines (point-min) (window-start)))
665 (old-history Info-history)
666 (new-history (and Info-current-file
667 (list Info-current-file Info-current-node (point)))))
668 (kill-buffer (current-buffer))
669 (Info-find-node filename nodename)
670 (setq Info-history old-history)
671 (if (and (equal old-filename Info-current-file)
672 (equal old-nodename Info-current-node))
673 (progn
674 ;; note goto-line is no good, we want to measure from point-min
cb7de2e7 675 (goto-char (point-min))
f584b4c7
RS
676 (forward-line wline)
677 (set-window-start (selected-window) (point))
cb7de2e7 678 (goto-char (point-min))
f584b4c7
RS
679 (forward-line pline)
680 (move-to-column pcolumn))
681 ;; only add to the history when coming from a different file+node
682 (if new-history
683 (setq Info-history (cons new-history Info-history))))))
684
357d11de
GM
685(defun Info-find-in-tag-table-1 (marker regexp case-fold)
686 "Find a node in a tag table.
687MARKER specifies the buffer and position to start searching at.
688REGEXP is a regular expression matching nodes or references. Its first
689group should match `Node:' or `Ref:'.
690CASE-FOLD t means search for a case-insensitive match.
691If a match was found, value is a list (FOUND-ANCHOR POS MODE), where
692FOUND-ANCHOR is non-nil if a `Ref:' was matched, POS is the position
693where the match was found, and MODE is `major-mode' of the buffer in
694which the match was found."
dde7f979 695 (let ((case-fold-search case-fold))
357d11de
GM
696 (save-excursion
697 (set-buffer (marker-buffer marker))
698 (goto-char marker)
921e5fe6 699
357d11de
GM
700 ;; Search tag table
701 (beginning-of-line)
702 (when (re-search-forward regexp nil t)
703 (list (string-equal "Ref:" (match-string 1))
365d2503 704 (+ (point-min) (read (current-buffer)))
357d11de
GM
705 major-mode)))))
706
707(defun Info-find-in-tag-table (marker regexp)
708 "Find a node in a tag table.
709MARKER specifies the buffer and position to start searching at.
710REGEXP is a regular expression matching nodes or references. Its first
711group should match `Node:' or `Ref:'.
712If a match was found, value is a list (FOUND-ANCHOR POS MODE), where
713FOUND-ANCHOR is non-nil if a `Ref:' was matched, POS is the position
714where the match was found, and MODE is `major-mode' of the buffer in
715which the match was found.
716This function tries to find a case-sensitive match first, then a
717case-insensitive match is tried."
718 (let ((result (Info-find-in-tag-table-1 marker regexp nil)))
719 (when (null (car result))
720 (setq result (Info-find-in-tag-table-1 marker regexp t)))
721 result))
722
723(defun Info-find-node-in-buffer-1 (regexp case-fold)
724 "Find a node or anchor in the current buffer.
725REGEXP is a regular expression matching nodes or references. Its first
726group should match `Node:' or `Ref:'.
727CASE-FOLD t means search for a case-insensitive match.
728Value is the position at which a match was found, or nil if not found."
729 (let ((case-fold-search case-fold)
730 found)
731 (save-excursion
732 (when (Info-node-at-bob-matching regexp)
733 (setq found (point)))
734 (while (and (not found)
735 (search-forward "\n\^_" nil t))
736 (forward-line 1)
737 (let ((beg (point)))
738 (forward-line 1)
739 (when (re-search-backward regexp beg t)
740 (beginning-of-line)
741 (setq found (point)))))
742 found)))
921e5fe6 743
357d11de
GM
744(defun Info-find-node-in-buffer (regexp)
745 "Find a node or anchor in the current buffer.
746REGEXP is a regular expression matching nodes or references. Its first
747group should match `Node:' or `Ref:'.
748Value is the position at which a match was found, or nil if not found.
749This function looks for a case-sensitive match first. If none is found,
750a case-insensitive match is tried."
751 (or (Info-find-node-in-buffer-1 regexp nil)
752 (Info-find-node-in-buffer-1 regexp t)))
921e5fe6 753
4fceda3c 754(defun Info-find-node-2 (filename nodename &optional no-going-back)
9e5c2f50 755 (buffer-disable-undo (current-buffer))
a384cab3
JB
756 (or (eq major-mode 'Info-mode)
757 (Info-mode))
758 (widen)
759 (setq Info-current-node nil)
760 (unwind-protect
8bdf4b20
KH
761 (let ((case-fold-search t)
762 anchorpos)
37a3ff5b
RS
763 ;; Switch files if necessary
764 (or (null filename)
765 (equal Info-current-file filename)
766 (let ((buffer-read-only nil))
767 (setq Info-current-file nil
768 Info-current-subfile nil
769 Info-current-file-completions nil
770 buffer-file-name nil)
771 (erase-buffer)
ab2f22ad
JH
772 (cond
773 ((eq filename t)
774 (Info-insert-dir))
775 ((eq filename 'apropos)
776 (insert-buffer-substring " *info-apropos*"))
89b064a9
JL
777 ((eq filename 'history)
778 (insert-buffer-substring " *info-history*"))
779 ((eq filename 'toc)
780 (insert-buffer-substring " *info-toc*"))
ab2f22ad 781 (t
f7d5479b 782 (info-insert-file-contents filename nil)
ab2f22ad 783 (setq default-directory (file-name-directory filename))))
37a3ff5b
RS
784 (set-buffer-modified-p nil)
785 ;; See whether file has a tag table. Record the location if yes.
786 (goto-char (point-max))
787 (forward-line -8)
788 ;; Use string-equal, not equal, to ignore text props.
789 (if (not (or (string-equal nodename "*")
790 (not
791 (search-forward "\^_\nEnd tag table\n" nil t))))
792 (let (pos)
793 ;; We have a tag table. Find its beginning.
794 ;; Is this an indirect file?
795 (search-backward "\nTag table:\n")
796 (setq pos (point))
797 (if (save-excursion
798 (forward-line 2)
799 (looking-at "(Indirect)\n"))
800 ;; It is indirect. Copy it to another buffer
801 ;; and record that the tag table is in that buffer.
802 (let ((buf (current-buffer))
803 (tagbuf
804 (or Info-tag-table-buffer
805 (generate-new-buffer " *info tag table*"))))
806 (setq Info-tag-table-buffer tagbuf)
807 (save-excursion
808 (set-buffer tagbuf)
9e5c2f50 809 (buffer-disable-undo (current-buffer))
37a3ff5b
RS
810 (setq case-fold-search t)
811 (erase-buffer)
812 (insert-buffer-substring buf))
813 (set-marker Info-tag-table-marker
814 (match-end 0) tagbuf))
815 (set-marker Info-tag-table-marker pos)))
816 (set-marker Info-tag-table-marker nil))
817 (setq Info-current-file
ab2f22ad
JH
818 (cond
819 ((eq filename t) "dir")
820 ((eq filename 'apropos) "apropos")
89b064a9
JL
821 ((eq filename 'history) "history")
822 ((eq filename 'toc) "toc")
ab2f22ad
JH
823 (t filename)))
824 ))
37a3ff5b
RS
825 ;; Use string-equal, not equal, to ignore text props.
826 (if (string-equal nodename "*")
827 (progn (setq Info-current-node nodename)
828 (Info-set-mode-line))
829 ;; Possibilities:
830 ;;
831 ;; 1. Anchor found in tag table
832 ;; 2. Anchor *not* in tag table
833 ;;
834 ;; 3. Node found in tag table
835 ;; 4. Node *not* found in tag table, but found in file
836 ;; 5. Node *not* in tag table, and *not* in file
837 ;;
838 ;; *Or* the same, but in an indirect subfile.
839
840 ;; Search file for a suitable node.
8bdf4b20 841 (let ((guesspos (point-min))
357d11de 842 (regexp (concat "\\(Node:\\|Ref:\\) *\\("
4ce5c223 843 (if (stringp nodename)
dd31e4e8
GM
844 (regexp-quote nodename)
845 "")
dde7f979 846 "\\) *[,\t\n\177]")))
37a3ff5b 847
82172606 848 (catch 'foo
921e5fe6 849
82172606 850 ;; First, search a tag table, if any
357d11de
GM
851 (when (marker-position Info-tag-table-marker)
852 (let* ((m Info-tag-table-marker)
853 (found (Info-find-in-tag-table m regexp)))
921e5fe6 854
357d11de
GM
855 (when found
856 ;; FOUND is (ANCHOR POS MODE).
857 (setq guesspos (nth 1 found))
921e5fe6 858
357d11de
GM
859 ;; If this is an indirect file, determine which
860 ;; file really holds this node and read it in.
861 (unless (eq (nth 2 found) 'Info-mode)
862 ;; Note that the current buffer must be the
863 ;; *info* buffer on entry to
864 ;; Info-read-subfile. Thus the hackery above.
865 (setq guesspos (Info-read-subfile guesspos)))
82172606
DL
866
867 ;; Handle anchor
357d11de
GM
868 (when (nth 0 found)
869 (goto-char (setq anchorpos guesspos))
870 (throw 'foo t)))))
82172606
DL
871
872 ;; Else we may have a node, which we search for:
873 (goto-char (max (point-min)
874 (- (byte-to-position guesspos) 1000)))
921e5fe6 875
357d11de
GM
876 ;; Now search from our advised position (or from beg of
877 ;; buffer) to find the actual node. First, check
878 ;; whether the node is right where we are, in case the
879 ;; buffer begins with a node.
880 (let ((pos (Info-find-node-in-buffer regexp)))
881 (when pos
882 (goto-char pos)
141b49f5
JL
883 (throw 'foo t)))
884
885 (when (string-match "\\([^.]+\\)\\." nodename)
886 (let (Info-point-loc)
887 (Info-find-node-2
888 filename (match-string 1 nodename) no-going-back))
889 (widen)
890 (throw 'foo t))
891
892 ;; No such anchor in tag table or node in tag table or file
893 (error "No such node or anchor: %s" nodename))
82172606
DL
894
895 (Info-select-node)
8a7757f6
JL
896 (goto-char (point-min))
897 (cond (anchorpos
898 (let ((new-history (list Info-current-file
899 (substring-no-properties nodename))))
900 ;; Add anchors to the history too
901 (setq Info-history-list
902 (cons new-history
903 (delete new-history Info-history-list))))
904 (goto-char anchorpos))
836bb478
JL
905 ((numberp Info-point-loc)
906 (forward-line (1- Info-point-loc))
907 (setq Info-point-loc nil))
908 ((stringp Info-point-loc)
909 (Info-find-index-name Info-point-loc)
910 (setq Info-point-loc nil))))))
a384cab3
JB
911 ;; If we did not finish finding the specified node,
912 ;; go back to the previous one.
4db579ab 913 (or Info-current-node no-going-back (null Info-history)
37a3ff5b
RS
914 (let ((hist (car Info-history)))
915 (setq Info-history (cdr Info-history))
916 (Info-find-node (nth 0 hist) (nth 1 hist) t)
917 (goto-char (nth 2 hist))))))
a384cab3 918
44c327f9
JB
919;; Cache the contents of the (virtual) dir file, once we have merged
920;; it for the first time, so we can save time subsequently.
7ea13762
RS
921(defvar Info-dir-contents nil)
922
44c327f9
JB
923;; Cache for the directory we decided to use for the default-directory
924;; of the merged dir text.
925(defvar Info-dir-contents-directory nil)
926
c142ab2d
RS
927;; Record the file attributes of all the files from which we
928;; constructed Info-dir-contents.
929(defvar Info-dir-file-attributes nil)
930
4fba3b2c
RS
931(defvar Info-dir-file-name nil)
932
7ea13762 933;; Construct the Info directory node by merging the files named `dir'
44c327f9
JB
934;; from various directories. Set the *info* buffer's
935;; default-directory to the first directory we actually get any text
936;; from.
7ea13762 937(defun Info-insert-dir ()
c142ab2d
RS
938 (if (and Info-dir-contents Info-dir-file-attributes
939 ;; Verify that none of the files we used has changed
940 ;; since we used it.
941 (eval (cons 'and
b461e32d
SM
942 (mapcar (lambda (elt)
943 (let ((curr (file-attributes
944 ;; Handle symlinks
945 (file-truename (car elt)))))
946
947 ;; Don't compare the access time.
948 (if curr (setcar (nthcdr 4 curr) 0))
949 (setcar (nthcdr 4 (cdr elt)) 0)
950 (equal (cdr elt) curr)))
c142ab2d 951 Info-dir-file-attributes))))
4fba3b2c
RS
952 (progn
953 (insert Info-dir-contents)
954 (goto-char (point-min)))
dd31e4e8 955 (let ((dirs (if Info-additional-directory-list
b34dcc82
RS
956 (append Info-directory-list
957 Info-additional-directory-list)
958 Info-directory-list))
1bd19a31 959 (dir-file-attrs nil)
1bcedb3b
RS
960 ;; Bind this in case the user sets it to nil.
961 (case-fold-search t)
4fba3b2c
RS
962 ;; This is set non-nil if we find a problem in some input files.
963 problems
f4008b6e 964 buffers buffer others nodes dirs-done)
44c327f9
JB
965
966 ;; Search the directory list for the directory file.
7ea13762 967 (while dirs
8d1abb42
RS
968 (let ((truename (file-truename (expand-file-name (car dirs)))))
969 (or (member truename dirs-done)
970 (member (directory-file-name truename) dirs-done)
971 ;; Try several variants of specified name.
972 ;; Try upcasing, appending `.info', or both.
825d6f08
RS
973 (let* (file
974 (attrs
975 (or
976 (progn (setq file (expand-file-name "dir" truename))
977 (file-attributes file))
978 (progn (setq file (expand-file-name "DIR" truename))
979 (file-attributes file))
980 (progn (setq file (expand-file-name "dir.info" truename))
981 (file-attributes file))
982 (progn (setq file (expand-file-name "DIR.INFO" truename))
983 (file-attributes file)))))
8d1abb42
RS
984 (setq dirs-done
985 (cons truename
986 (cons (directory-file-name truename)
987 dirs-done)))
825d6f08
RS
988 (if attrs
989 (save-excursion
990 (or buffers
991 (message "Composing main Info directory..."))
2d41cf59 992 (set-buffer (generate-new-buffer " info dir"))
9d499629
RS
993 (condition-case nil
994 (progn
995 (insert-file-contents file)
1bd19a31
MB
996 (set (make-local-variable 'Info-dir-file-name)
997 file)
6af7040d 998 (push (current-buffer) buffers)
1bd19a31 999 (push (cons file attrs) dir-file-attrs))
9d499629 1000 (error (kill-buffer (current-buffer))))))))
1bd19a31
MB
1001 (unless (cdr dirs)
1002 (set (make-local-variable 'Info-dir-contents-directory)
1003 (file-name-as-directory (car dirs))))
825d6f08 1004 (setq dirs (cdr dirs))))
399c88ad 1005
4db579ab 1006 (or buffers
81e14cb2 1007 (error "Can't find the Info directory node"))
1bd19a31 1008
44c327f9 1009 ;; Distinguish the dir file that comes with Emacs from all the
f4008b6e 1010 ;; others. Yes, that is really what this is supposed to do.
f601cd3e
DL
1011 ;; The definition of `Info-directory-list' puts it first on that
1012 ;; list and so last in `buffers' at this point.
1013 (setq buffer (car (last buffers))
1014 others (delq buffer buffers))
44c327f9 1015
debcea0c
KH
1016 ;; Insert the entire original dir file as a start; note that we've
1017 ;; already saved its default directory to use as the default
1018 ;; directory for the whole concatenation.
7ea13762 1019 (insert-buffer buffer)
44c327f9 1020
7ea13762 1021 ;; Look at each of the other buffers one by one.
6af7040d
SM
1022 (dolist (other others)
1023 (let (this-buffer-nodes)
7ea13762 1024 ;; In each, find all the menus.
6af7040d 1025 (with-current-buffer other
7ea13762
RS
1026 (goto-char (point-min))
1027 ;; Find each menu, and add an elt to NODES for it.
1028 (while (re-search-forward "^\\* Menu:" nil t)
6af7040d
SM
1029 (while (and (zerop (forward-line 1)) (eolp)))
1030 (let ((beg (point))
1031 nodename end)
1032 (re-search-backward "^\^_")
7ea13762 1033 (search-forward "Node: ")
89b064a9 1034 (setq nodename (Info-following-node-name))
3a6ade8a 1035 (search-forward "\n\^_" nil 'move)
7ea13762
RS
1036 (beginning-of-line)
1037 (setq end (point))
6af7040d 1038 (push (list nodename other beg end) this-buffer-nodes)))
9f4aee27 1039 (if (assoc-string "top" this-buffer-nodes t)
4fba3b2c
RS
1040 (setq nodes (nconc this-buffer-nodes nodes))
1041 (setq problems t)
6af7040d 1042 (message "No `top' node in %s" Info-dir-file-name)))))
fdf4b680 1043 ;; Add to the main menu a menu item for each other node.
6af7040d 1044 (re-search-forward "^\\* Menu:")
7ea13762
RS
1045 (forward-line 1)
1046 (let ((menu-items '("top"))
3a6ade8a 1047 (end (save-excursion (search-forward "\^_" nil t) (point))))
6af7040d
SM
1048 (dolist (node nodes)
1049 (let ((nodename (car node)))
c3a29d70
RS
1050 (save-excursion
1051 (or (member (downcase nodename) menu-items)
b37daea4 1052 (re-search-forward (concat "^\\* +"
c3a29d70
RS
1053 (regexp-quote nodename)
1054 "::")
1055 end t)
1056 (progn
1057 (insert "* " nodename "::" "\n")
6af7040d 1058 (push nodename menu-items)))))))
7ea13762
RS
1059 ;; Now take each node of each of the other buffers
1060 ;; and merge it into the main buffer.
6af7040d 1061 (dolist (node nodes)
760d5cb3 1062 (let ((case-fold-search t)
6af7040d 1063 (nodename (car node)))
7ea13762
RS
1064 (goto-char (point-min))
1065 ;; Find the like-named node in the main buffer.
ba42ce14 1066 (if (re-search-forward (concat "^\^_.*\n.*Node: "
7ea13762
RS
1067 (regexp-quote nodename)
1068 "[,\n\t]")
1069 nil t)
1070 (progn
3a6ade8a 1071 (search-forward "\n\^_" nil 'move)
25869acf
RS
1072 (beginning-of-line)
1073 (insert "\n"))
7ea13762
RS
1074 ;; If none exists, add one.
1075 (goto-char (point-max))
dfbf6104 1076 (insert "\^_\nFile: dir\tNode: " nodename "\n\n* Menu:\n\n"))
7ea13762
RS
1077 ;; Merge the text from the other buffer's menu
1078 ;; into the menu in the like-named node in the main buffer.
6af7040d
SM
1079 (apply 'insert-buffer-substring (cdr node))))
1080 (Info-dir-remove-duplicates)
dc9bec22
TTN
1081 ;; Kill all the buffers we just made, including the special one excised.
1082 (mapc 'kill-buffer (cons buffer buffers))
4fba3b2c
RS
1083 (goto-char (point-min))
1084 (if problems
1085 (message "Composing main Info directory...problems encountered, see `*Messages*'")
1bd19a31
MB
1086 (message "Composing main Info directory...done"))
1087 (set (make-local-variable 'Info-dir-contents) (buffer-string))
1088 (set (make-local-variable 'Info-dir-file-attributes) dir-file-attrs)))
44c327f9 1089 (setq default-directory Info-dir-contents-directory))
7ea13762 1090
6af7040d
SM
1091(defvar Info-streamline-headings
1092 '(("Emacs" . "Emacs")
1093 ("Programming" . "Programming")
1094 ("Libraries" . "Libraries")
1095 ("World Wide Web\\|Net Utilities" . "Net Utilities"))
1096 "List of elements (RE . NAME) to merge headings matching RE to NAME.")
1097
1098(defun Info-dir-remove-duplicates ()
1099 (let (limit)
1100 (goto-char (point-min))
1101 ;; Remove duplicate headings in the same menu.
1102 (while (search-forward "\n* Menu:" nil t)
141b49f5 1103 (setq limit (save-excursion (search-forward "\n\^_" nil t)))
6af7040d
SM
1104 ;; Look for the next heading to unify.
1105 (while (re-search-forward "^\\(\\w.*\\)\n\\*" limit t)
1106 (let ((name (match-string 1))
1107 (start (match-beginning 0))
1108 (entries nil) re)
1109 ;; Check whether this heading should be streamlined.
1110 (save-match-data
1111 (dolist (x Info-streamline-headings)
1112 (when (string-match (car x) name)
1113 (setq name (cdr x))
1114 (setq re (car x)))))
1115 (if re (replace-match name t t nil 1))
1116 (goto-char (if (re-search-forward "^[^* \n\t]" limit t)
1117 (match-beginning 0)
1118 (or limit (point-max))))
1119 ;; Look for other headings of the same category and merge them.
1120 (save-excursion
1121 (while (re-search-forward "^\\(\\w.*\\)\n\\*" limit t)
1122 (when (if re (save-match-data (string-match re (match-string 1)))
1123 (equal name (match-string 1)))
1124 (forward-line 0)
1125 ;; Delete redundant heading.
1126 (delete-region (match-beginning 0) (point))
1127 ;; Push the entries onto `text'.
1128 (push
1129 (delete-and-extract-region
1130 (point)
1131 (if (re-search-forward "^[^* \n\t]" nil t)
1132 (match-beginning 0)
1133 (or limit (point-max)))) entries))))
1134 ;; Insert the entries just found.
1135 (while (= (line-beginning-position 0) (1- (point)))
1136 (backward-char))
1137 (dolist (entry (nreverse entries))
1138 (insert entry)
1139 (while (= (line-beginning-position 0) (1- (point)))
1140 (delete-region (1- (point)) (point))))
921e5fe6 1141
6af7040d
SM
1142 ;; Now remove duplicate entries under the same heading.
1143 (let ((seen nil)
1144 (limit (point)))
1145 (goto-char start)
1146 (while (re-search-forward "^* \\([^:\n]+:\\(:\\|[^.\n]+\\).\\)"
1147 limit 'move)
1148 (let ((x (match-string 1)))
1149 (if (member-ignore-case x seen)
1150 (delete-region (match-beginning 0)
1151 (progn (re-search-forward "^[^ \t]" nil t)
25046503 1152 (match-beginning 0)))
6af7040d
SM
1153 (push x seen))))))))))
1154
c5fe2ff1
RS
1155;; Note that on entry to this function the current-buffer must be the
1156;; *info* buffer; not the info tags buffer.
a384cab3 1157(defun Info-read-subfile (nodepos)
a361deba
RS
1158 ;; NODEPOS is either a position (in the Info file as a whole,
1159 ;; not relative to a subfile) or the name of a subfile.
a384cab3
JB
1160 (let (lastfilepos
1161 lastfilename)
a361deba
RS
1162 (if (numberp nodepos)
1163 (save-excursion
1164 (set-buffer (marker-buffer Info-tag-table-marker))
1165 (goto-char (point-min))
ba42ce14
EZ
1166 (or (looking-at "\^_")
1167 (search-forward "\n\^_"))
a361deba
RS
1168 (forward-line 2)
1169 (catch 'foo
1170 (while (not (looking-at "\^_"))
1171 (if (not (eolp))
1172 (let ((beg (point))
1173 thisfilepos thisfilename)
1174 (search-forward ": ")
1175 (setq thisfilename (buffer-substring beg (- (point) 2)))
365d2503 1176 (setq thisfilepos (+ (point-min) (read (current-buffer))))
a361deba
RS
1177 ;; read in version 19 stops at the end of number.
1178 ;; Advance to the next line.
1179 (forward-line 1)
1180 (if (> thisfilepos nodepos)
1181 (throw 'foo t))
1182 (setq lastfilename thisfilename)
1183 (setq lastfilepos thisfilepos))
1184 (forward-line 1)))))
1185 (setq lastfilename nodepos)
1186 (setq lastfilepos 0))
c5fe2ff1
RS
1187 ;; Assume previous buffer is in Info-mode.
1188 ;; (set-buffer (get-buffer "*info*"))
a384cab3
JB
1189 (or (equal Info-current-subfile lastfilename)
1190 (let ((buffer-read-only nil))
1191 (setq buffer-file-name nil)
1192 (widen)
1193 (erase-buffer)
1143a6b0 1194 (info-insert-file-contents lastfilename)
a384cab3
JB
1195 (set-buffer-modified-p nil)
1196 (setq Info-current-subfile lastfilename)))
b0e52598
RS
1197 ;; Widen in case we are in the same subfile as before.
1198 (widen)
a384cab3 1199 (goto-char (point-min))
ba42ce14
EZ
1200 (if (looking-at "\^_")
1201 (forward-char 1)
1202 (search-forward "\n\^_"))
a361deba
RS
1203 (if (numberp nodepos)
1204 (+ (- nodepos lastfilepos) (point)))))
a384cab3 1205
ec9b1372 1206(defun Info-unescape-quotes (value)
1ed6e0c3 1207 "Unescape double quotes and backslashes in VALUE."
ec9b1372
KB
1208 (let ((start 0)
1209 (unquote value))
1210 (while (string-match "[^\\\"]*\\(\\\\\\)[\\\\\"]" unquote start)
1211 (setq unquote (replace-match "" t t unquote 1))
1212 (setq start (- (match-end 0) 1)))
1213 unquote))
1214
1215;; As of Texinfo 4.6, makeinfo writes constructs like
1216;; \0\h[image param=value ...\h\0]
1217;; into the Info file for handling images.
1218(defun Info-split-parameter-string (parameter-string)
1219 "Return alist of (\"KEY\" . \"VALUE\") from PARAMETER-STRING; a
1ed6e0c3
JPW
1220whitespace separated list of KEY=VALUE pairs. If VALUE contains
1221whitespace or double quotes, it must be quoted in double quotes and
1222any double quotes or backslashes must be escaped (\\\",\\\\)."
ec9b1372
KB
1223 (let ((start 0)
1224 (parameter-alist))
1225 (while (string-match
1226 "\\s *\\([^=]+\\)=\\(?:\\([^\\s \"]+\\)\\|\\(?:\"\\(\\(?:[^\\\"]\\|\\\\[\\\\\"]\\)*\\)\"\\)\\)"
1227 parameter-string start)
1228 (setq start (match-end 0))
1229 (push (cons (match-string 1 parameter-string)
1230 (or (match-string 2 parameter-string)
1231 (Info-unescape-quotes
1232 (match-string 3 parameter-string))))
1233 parameter-alist))
1234 parameter-alist))
1235
1236(defun Info-display-images-node ()
1237 "Display images in current node."
1238 (save-excursion
1239 (let ((inhibit-read-only t)
dde7f979 1240 (case-fold-search t))
ec9b1372
KB
1241 (goto-char (point-min))
1242 (while (re-search-forward
1243 "\\(\0\b[[]image\\(\\(?:[^\b]\\|[^\0]+\b\\)*\\)\0\b[]]\\)"
1244 nil t)
1245 (let* ((start (match-beginning 1))
1246 (parameter-alist (Info-split-parameter-string (match-string 2)))
1247 (src (cdr (assoc-string "src" parameter-alist)))
1248 (image-file (if src (if (file-name-absolute-p src) src
1249 (concat default-directory src))
1250 ""))
1251 (image (if (file-exists-p image-file)
1252 (create-image image-file)
1253 "[broken image]")))
ec9b1372
KB
1254 (if (not (get-text-property start 'display))
1255 (add-text-properties
1256 start (point) `(display ,image rear-nonsticky (display)))))))
1257 (set-buffer-modified-p nil)))
1258
0c1b7af5
JL
1259;; Texinfo 4.7 adds cookies of the form ^@^H[NAME CONTENTS ^@^H].
1260;; Hide any construct of the general form ^@[^@-^_][ ... ^@[^@-^_]],
1261;; including one optional trailing newline.
1262(defun Info-hide-cookies-node ()
1263 "Hide unrecognised cookies in current node."
1264 (save-excursion
1265 (let ((inhibit-read-only t)
1266 (case-fold-search t))
1267 (goto-char (point-min))
1268 (while (re-search-forward
1269 "\\(\0[\0-\37][[][^\0]*\0[\0-\37][]]\n?\\)"
1270 nil t)
1271 (let* ((start (match-beginning 1)))
1272 (if (not (get-text-property start 'invisible))
1273 (put-text-property start (point) 'invisible t)))))
1274 (set-buffer-modified-p nil)))
1275
a384cab3 1276(defun Info-select-node ()
b038485e
AS
1277 "Select the info node that point is in."
1278 ;; Bind this in case the user sets it to nil.
1bcedb3b
RS
1279 (let ((case-fold-search t))
1280 (save-excursion
760d5cb3
GM
1281 ;; Find beginning of node.
1282 (if (search-backward "\n\^_" nil 'move)
1283 (forward-line 2)
1284 (if (looking-at "\^_")
1285 (forward-line 1)
1286 (signal 'search-failed (list "\n\^_"))))
1287 ;; Get nodename spelled as it is in the node.
1288 (re-search-forward "Node:[ \t]*")
1289 (setq Info-current-node
1290 (buffer-substring-no-properties (point)
1291 (progn
1292 (skip-chars-forward "^,\t\n")
1293 (point))))
1294 (Info-set-mode-line)
1295 ;; Find the end of it, and narrow.
1296 (beginning-of-line)
1297 (let (active-expression)
50ac70af 1298 ;; Narrow to the node contents
760d5cb3
GM
1299 (narrow-to-region (point)
1300 (if (re-search-forward "\n[\^_\f]" nil t)
1301 (prog1
1302 (1- (point))
1303 (if (looking-at "[\n\^_\f]*execute: ")
1304 (progn
1305 (goto-char (match-end 0))
1306 (setq active-expression
1307 (read (current-buffer))))))
1308 (point-max)))
1309 (if Info-enable-active-nodes (eval active-expression))
8a7757f6
JL
1310 ;; Add a new unique history item to full history list
1311 (let ((new-history (list Info-current-file Info-current-node)))
1312 (setq Info-history-list
424b9d3e
JL
1313 (cons new-history (delete new-history Info-history-list)))
1314 (setq Info-history-forward nil))
141b49f5
JL
1315 (if (not (eq Info-fontify-maximum-menu-size nil))
1316 (Info-fontify-node))
ec9b1372 1317 (Info-display-images-node)
0c1b7af5 1318 (Info-hide-cookies-node)
760d5cb3 1319 (run-hooks 'Info-selection-hook)))))
a384cab3
JB
1320
1321(defun Info-set-mode-line ()
1322 (setq mode-line-buffer-identification
313fe58a 1323 (nconc (propertized-buffer-identification "%b")
918e364a
MB
1324 (list
1325 (concat " ("
1326 (file-name-nondirectory
1327 (if (stringp Info-current-file)
1328 Info-current-file
1329 (or buffer-file-name "")))
1330 ") "
1331 (or Info-current-node ""))))))
a384cab3
JB
1332\f
1333;; Go to an info node specified with a filename-and-nodename string
1334;; of the sort that is found in pointers in nodes.
1335
bc224645 1336;;;###autoload
4fceda3c 1337(defun Info-goto-node (nodename &optional fork)
cfc697a2 1338 "Go to info node named NODENAME. Give just NODENAME or (FILENAME)NODENAME.
dc414be1
EZ
1339If NODENAME is of the form (FILENAME)NODENAME, the node is in the Info file
1340FILENAME; otherwise, NODENAME should be in the current Info file (or one of
1341its sub-files).
1342Completion is available, but only for node names in the current Info file.
7210b6f5
DL
1343If FORK is non-nil (interactively with a prefix arg), show the node in
1344a new info buffer.
4fceda3c 1345If FORK is a string, it is the name to use for the new buffer."
d1268e52 1346 (interactive (list (Info-read-node-name "Go to node: ") current-prefix-arg))
d0e41cba 1347 (info-initialize)
4fceda3c 1348 (if fork
760d5cb3
GM
1349 (set-buffer
1350 (clone-buffer (concat "*info-" (if (stringp fork) fork nodename) "*") t)))
a384cab3
JB
1351 (let (filename)
1352 (string-match "\\s *\\((\\s *\\([^\t)]*\\)\\s *)\\s *\\|\\)\\(.*\\)"
1353 nodename)
1354 (setq filename (if (= (match-beginning 1) (match-end 1))
1355 ""
c60c12be
SM
1356 (match-string 2 nodename))
1357 nodename (match-string 3 nodename))
836bb478 1358 (let ((trim (string-match "\\s +\\'" filename)))
a384cab3 1359 (if trim (setq filename (substring filename 0 trim))))
836bb478 1360 (let ((trim (string-match "\\s +\\'" nodename)))
a384cab3 1361 (if trim (setq nodename (substring nodename 0 trim))))
e579232c 1362 (if transient-mark-mode (deactivate-mark))
a384cab3
JB
1363 (Info-find-node (if (equal filename "") nil filename)
1364 (if (equal nodename "") "Top" nodename))))
552775bd 1365
ddf89211
RS
1366(defvar Info-read-node-completion-table)
1367
03524be6 1368;; This function is used as the "completion table" while reading a node name.
ddf89211 1369;; It does completion using the alist in Info-read-node-completion-table
03524be6
RS
1370;; unless STRING starts with an open-paren.
1371(defun Info-read-node-name-1 (string predicate code)
365d2503
SM
1372 (cond
1373 ;; First complete embedded file names.
1374 ((string-match "\\`([^)]*\\'" string)
1375 (let ((file (substring string 1)))
1376 (cond
1377 ((eq code nil)
1378 (let ((comp (try-completion file 'locate-file-completion
1379 (cons Info-directory-list
1380 (mapcar 'car Info-suffix-list)))))
1381 (cond
1382 ((eq comp t) (concat string ")"))
1383 (comp (concat "(" comp)))))
1384 ((eq code t) (all-completions file 'locate-file-completion
1385 (cons Info-directory-list
1386 (mapcar 'car Info-suffix-list))))
1387 (t nil))))
1388 ;; If a file name was given, then any node is fair game.
1389 ((string-match "\\`(" string)
1390 (cond
1391 ((eq code nil) string)
1392 ((eq code t) nil)
1393 (t t)))
1394 ;; Otherwise use Info-read-node-completion-table.
1395 ((eq code nil)
1396 (try-completion string Info-read-node-completion-table predicate))
1397 ((eq code t)
1398 (all-completions string Info-read-node-completion-table predicate))
1399 (t
1400 (test-completion string Info-read-node-completion-table predicate))))
03524be6 1401
552775bd
RS
1402(defun Info-read-node-name (prompt &optional default)
1403 (let* ((completion-ignore-case t)
ddf89211 1404 (Info-read-node-completion-table (Info-build-node-completions))
b6d61ffa 1405 (nodename (completing-read prompt 'Info-read-node-name-1 nil t)))
552775bd
RS
1406 (if (equal nodename "")
1407 (or default
1408 (Info-read-node-name prompt))
1409 nodename)))
1410
1411(defun Info-build-node-completions ()
1412 (or Info-current-file-completions
1bcedb3b
RS
1413 (let ((compl nil)
1414 ;; Bind this in case the user sets it to nil.
11638562
EZ
1415 (case-fold-search t)
1416 (node-regexp "Node: *\\([^,\n]*\\) *[,\n\t]"))
552775bd
RS
1417 (save-excursion
1418 (save-restriction
1419 (if (marker-buffer Info-tag-table-marker)
c5fe2ff1
RS
1420 (let ((marker Info-tag-table-marker))
1421 (set-buffer (marker-buffer marker))
cedb118c 1422 (widen)
c5fe2ff1 1423 (goto-char marker)
fdf4b680 1424 (while (re-search-forward "\n\\(Node\\|Ref\\): \\(.*\\)\177" nil t)
552775bd 1425 (setq compl
fdf4b680 1426 (cons (list (match-string-no-properties 2))
552775bd
RS
1427 compl))))
1428 (widen)
1429 (goto-char (point-min))
11638562
EZ
1430 ;; If the buffer begins with a node header, process that first.
1431 (if (Info-node-at-bob-matching node-regexp)
110d4e80 1432 (setq compl (list (match-string-no-properties 1))))
11638562 1433 ;; Now for the rest of the nodes.
552775bd
RS
1434 (while (search-forward "\n\^_" nil t)
1435 (forward-line 1)
1436 (let ((beg (point)))
1437 (forward-line 1)
11638562 1438 (if (re-search-backward node-regexp beg t)
399c88ad 1439 (setq compl
110d4e80 1440 (cons (list (match-string-no-properties 1))
552775bd 1441 compl))))))))
87d2b5a2 1442 (setq compl (cons '("*") compl))
07b1b912 1443 (set (make-local-variable 'Info-current-file-completions) compl))))
a384cab3 1444\f
aea2a8da
JB
1445(defun Info-restore-point (hl)
1446 "If this node has been visited, restore the point value when we left."
cedb118c
RS
1447 (while hl
1448 (if (and (equal (nth 0 (car hl)) Info-current-file)
e90d1271
RS
1449 ;; Use string-equal, not equal, to ignore text props.
1450 (string-equal (nth 1 (car hl)) Info-current-node))
cedb118c 1451 (progn
d96504df
KH
1452 (goto-char (nth 2 (car hl)))
1453 (setq hl nil)) ;terminate the while at next iter
cedb118c 1454 (setq hl (cdr hl)))))
aea2a8da 1455\f
2c80a618
GM
1456(defvar Info-search-history nil
1457 "The history list for `Info-search'.")
a384cab3 1458
8a7757f6
JL
1459(defvar Info-search-case-fold nil
1460 "The value of `case-fold-search' from previous `Info-search' command.")
1461
0b02cda9
JL
1462(defun Info-search (regexp &optional bound noerror count direction)
1463 "Search for REGEXP, starting from point, and select node it's found in.
1464If DIRECTION is `backward', search in the reverse direction."
bbc96600
EZ
1465 (interactive (list (read-string
1466 (if Info-search-history
8a7757f6
JL
1467 (format "Regexp search%s (default `%s'): "
1468 (if case-fold-search "" " case-sensitively")
bbc96600 1469 (car Info-search-history))
8a7757f6
JL
1470 (format "Regexp search%s: "
1471 (if case-fold-search "" " case-sensitively")))
bbc96600 1472 nil 'Info-search-history)))
2c80a618
GM
1473 (when transient-mark-mode
1474 (deactivate-mark))
1475 (when (equal regexp "")
1476 (setq regexp (car Info-search-history)))
c47b5bbe 1477 (when regexp
774f8aee 1478 (let (found beg-found give-up
0b02cda9 1479 (backward (eq direction 'backward))
c47b5bbe
DL
1480 (onode Info-current-node)
1481 (ofile Info-current-file)
1482 (opoint (point))
0b02cda9
JL
1483 (opoint-min (point-min))
1484 (opoint-max (point-max))
c47b5bbe
DL
1485 (ostart (window-start))
1486 (osubfile Info-current-subfile))
8a7757f6 1487 (setq Info-search-case-fold case-fold-search)
c47b5bbe
DL
1488 (save-excursion
1489 (save-restriction
1490 (widen)
ca53db33
JL
1491 (when backward
1492 ;; Hide Info file header for backward search
1493 (narrow-to-region (save-excursion
1494 (goto-char (point-min))
1495 (search-forward "\n\^_")
1496 (1- (point)))
1497 (point-max)))
774f8aee 1498 (while (and (not give-up)
be4b3ae8
JL
1499 (save-match-data
1500 (or (null found)
1501 (if backward
1502 (isearch-range-invisible found beg-found)
1503 (isearch-range-invisible beg-found found))
1504 ;; Skip node header line
180ee7c7
JL
1505 (and (save-excursion (forward-line -1)
1506 (looking-at "\^_"))
866920e3 1507 (forward-line (if backward -1 1)))
be4b3ae8
JL
1508 ;; Skip Tag Table node
1509 (save-excursion
1510 (and (search-backward "\^_" nil t)
1511 (looking-at "\^_\nTag Table"))))))
180ee7c7 1512 (let ((search-spaces-regexp Info-search-whitespace-regexp))
5d4faeda
RS
1513 (if (if backward
1514 (re-search-backward regexp bound t)
1515 (re-search-forward regexp bound t))
1516 (setq found (point) beg-found (if backward (match-end 0)
1517 (match-beginning 0)))
1518 (setq give-up t))))))
d457219d
JL
1519
1520 (when (and isearch-mode Info-isearch-search
1521 (not Info-isearch-initial-node)
1522 (not bound)
1523 (or give-up (and found (not (and (> found opoint-min)
1524 (< found opoint-max))))))
1525 (signal 'search-failed (list regexp "initial node")))
1526
774f8aee
RS
1527 ;; If no subfiles, give error now.
1528 (if give-up
c47b5bbe 1529 (if (null Info-current-subfile)
180ee7c7
JL
1530 (let ((search-spaces-regexp Info-search-whitespace-regexp))
1531 (if backward
1532 (re-search-backward regexp)
1533 (re-search-forward regexp)))
774f8aee
RS
1534 (setq found nil)))
1535
d457219d
JL
1536 (if (and bound (not found))
1537 (signal 'search-failed (list regexp)))
1538
0b02cda9 1539 (unless (or found bound)
774f8aee
RS
1540 (unwind-protect
1541 ;; Try other subfiles.
1542 (let ((list ()))
1543 (save-excursion
1544 (set-buffer (marker-buffer Info-tag-table-marker))
1545 (goto-char (point-min))
1546 (search-forward "\n\^_\nIndirect:")
1547 (save-restriction
1548 (narrow-to-region (point)
1549 (progn (search-forward "\n\^_")
1550 (1- (point))))
c5fe2ff1 1551 (goto-char (point-min))
774f8aee
RS
1552 ;; Find the subfile we just searched.
1553 (search-forward (concat "\n" osubfile ": "))
1554 ;; Skip that one.
0b02cda9 1555 (forward-line (if backward 0 1))
866920e3 1556 (if backward (forward-char -1))
774f8aee
RS
1557 ;; Make a list of all following subfiles.
1558 ;; Each elt has the form (VIRT-POSITION . SUBFILENAME).
0b02cda9
JL
1559 (while (not (if backward (bobp) (eobp)))
1560 (if backward
1561 (re-search-backward "\\(^.*\\): [0-9]+$")
1562 (re-search-forward "\\(^.*\\): [0-9]+$"))
774f8aee
RS
1563 (goto-char (+ (match-end 1) 2))
1564 (setq list (cons (cons (+ (point-min)
1565 (read (current-buffer)))
1566 (match-string-no-properties 1))
1567 list))
0b02cda9
JL
1568 (goto-char (if backward
1569 (1- (match-beginning 0))
1570 (1+ (match-end 0)))))
774f8aee
RS
1571 ;; Put in forward order
1572 (setq list (nreverse list))))
1573 (while list
1574 (message "Searching subfile %s..." (cdr (car list)))
1575 (Info-read-subfile (car (car list)))
ca53db33
JL
1576 (when backward
1577 ;; Hide Info file header for backward search
1578 (narrow-to-region (save-excursion
1579 (goto-char (point-min))
1580 (search-forward "\n\^_")
1581 (1- (point)))
1582 (point-max))
1583 (goto-char (point-max)))
774f8aee
RS
1584 (setq list (cdr list))
1585 (setq give-up nil found nil)
1586 (while (and (not give-up)
be4b3ae8
JL
1587 (save-match-data
1588 (or (null found)
1589 (if backward
1590 (isearch-range-invisible found beg-found)
1591 (isearch-range-invisible beg-found found))
1592 ;; Skip node header line
180ee7c7
JL
1593 (and (save-excursion (forward-line -1)
1594 (looking-at "\^_"))
866920e3 1595 (forward-line (if backward -1 1)))
be4b3ae8
JL
1596 ;; Skip Tag Table node
1597 (save-excursion
1598 (and (search-backward "\^_" nil t)
1599 (looking-at "\^_\nTag Table"))))))
180ee7c7
JL
1600 (let ((search-spaces-regexp Info-search-whitespace-regexp))
1601 (if (if backward
1602 (re-search-backward regexp nil t)
1603 (re-search-forward regexp nil t))
1604 (setq found (point) beg-found (if backward (match-end 0)
1605 (match-beginning 0)))
1606 (setq give-up t))))
774f8aee
RS
1607 (if give-up
1608 (setq found nil))
c47b5bbe 1609 (if found
774f8aee
RS
1610 (setq list nil)))
1611 (if found
1612 (message "")
1613 (signal 'search-failed (list regexp))))
1614 (if (not found)
1615 (progn (Info-read-subfile osubfile)
1616 (goto-char opoint)
1617 (Info-select-node)
1618 (set-window-start (selected-window) ostart)))))
0b02cda9
JL
1619
1620 (if (and (string= osubfile Info-current-subfile)
1621 (> found opoint-min)
1622 (< found opoint-max))
1623 ;; Search landed in the same node
1624 (goto-char found)
1625 (widen)
1626 (goto-char found)
1627 (save-match-data (Info-select-node)))
1628
760d5cb3
GM
1629 ;; Use string-equal, not equal, to ignore text props.
1630 (or (and (string-equal onode Info-current-node)
1631 (equal ofile Info-current-file))
866920e3
JL
1632 (and isearch-mode isearch-wrapped
1633 (eq opoint (if isearch-forward opoint-min opoint-max)))
760d5cb3
GM
1634 (setq Info-history (cons (list ofile onode opoint)
1635 Info-history))))))
8a7757f6
JL
1636
1637(defun Info-search-case-sensitively ()
1638 "Search for a regexp case-sensitively."
1639 (interactive)
1640 (let ((case-fold-search nil))
1641 (call-interactively 'Info-search)))
1642
1643(defun Info-search-next ()
1644 "Search for next regexp from a previous `Info-search' command."
1645 (interactive)
1646 (let ((case-fold-search Info-search-case-fold))
1647 (if Info-search-history
1648 (Info-search (car Info-search-history))
1649 (call-interactively 'Info-search))))
0b02cda9
JL
1650
1651(defun Info-search-backward (regexp &optional bound noerror count)
1652 "Search for REGEXP in the reverse direction."
1653 (interactive (list (read-string
1654 (if Info-search-history
1655 (format "Regexp search%s backward (default `%s'): "
1656 (if case-fold-search "" " case-sensitively")
1657 (car Info-search-history))
1658 (format "Regexp search%s backward: "
1659 (if case-fold-search "" " case-sensitively")))
1660 nil 'Info-search-history)))
1661 (Info-search regexp bound noerror count 'backward))
1662
1663(defun Info-isearch-search ()
9553cf63 1664 (if Info-isearch-search
98e77be5 1665 (lambda (string &optional bound noerror count)
d457219d
JL
1666 (if isearch-word
1667 (Info-search (concat "\\b" (replace-regexp-in-string
1668 "\\W+" "\\\\W+"
1669 (replace-regexp-in-string
1670 "^\\W+\\|\\W+$" "" string)) "\\b")
1671 bound noerror count
1672 (unless isearch-forward 'backward))
1673 (Info-search (if isearch-regexp string (regexp-quote string))
1674 bound noerror count
1675 (unless isearch-forward 'backward))
1676 (point)))
98e77be5
JL
1677 (let ((isearch-search-fun-function nil))
1678 (isearch-search-fun))))
0b02cda9
JL
1679
1680(defun Info-isearch-wrap ()
d457219d
JL
1681 (if Info-isearch-search
1682 (if Info-isearch-initial-node
1683 (progn
1684 (if isearch-forward (Info-top-node) (Info-final-node))
1685 (goto-char (if isearch-forward (point-min) (point-max))))
1686 (setq Info-isearch-initial-node Info-current-node)
1687 (setq isearch-wrapped nil))
0b02cda9
JL
1688 (goto-char (if isearch-forward (point-min) (point-max)))))
1689
1690(defun Info-isearch-push-state ()
1691 `(lambda (cmd)
1692 (Info-isearch-pop-state cmd ,Info-current-file ,Info-current-node)))
1693
1694(defun Info-isearch-pop-state (cmd file node)
1695 (or (and (string= Info-current-file file)
1696 (string= Info-current-node node))
1697 (progn (Info-find-node file node) (sit-for 0))))
1698
d457219d
JL
1699(defun Info-isearch-start ()
1700 (setq Info-isearch-initial-node nil))
a384cab3 1701\f
a384cab3 1702(defun Info-extract-pointer (name &optional errorname)
fdf4b680
DL
1703 "Extract the value of the node-pointer named NAME.
1704If there is none, use ERRORNAME in the error message;
c60c12be
SM
1705if ERRORNAME is nil, just return nil."
1706 ;; Bind this in case the user sets it to nil.
1bcedb3b
RS
1707 (let ((case-fold-search t))
1708 (save-excursion
c60c12be
SM
1709 (goto-char (point-min))
1710 (let ((bound (point)))
1711 (forward-line 1)
1712 (cond ((re-search-backward
1713 (concat name ":" (Info-following-node-name-re)) bound t)
1714 (match-string 1))
1715 ((not (eq errorname t))
1716 (error "Node has no %s"
1717 (capitalize (or errorname name)))))))))
1718
1719(defun Info-following-node-name-re (&optional allowedchars)
1720 "Return a regexp matching a node name.
fdf4b680 1721ALLOWEDCHARS, if non-nil, goes within [...] to make a regexp
c60c12be
SM
1722saying which chars may appear in the node name.
1723Submatch 1 is the complete node name.
1724Submatch 2 if non-nil is the parenthesized file name part of the node name.
1725Submatch 3 is the local part of the node name.
1726End of submatch 0, 1, and 3 are the same, so you can safely concat."
1727 (concat "[ \t]*" ;Skip leading space.
1728 "\\(\\(([^)]+)\\)?" ;Node name can start with a file name.
1729 "\\([" (or allowedchars "^,\t\n") "]*" ;Any number of allowed chars.
1730 "[" (or allowedchars "^,\t\n") " ]" ;The last char can't be a space.
1731 "\\|\\)\\)")) ;Allow empty node names.
a384cab3 1732
718e6bc7
RS
1733;;; For compatibility; other files have used this name.
1734(defun Info-following-node-name ()
1735 (and (looking-at (Info-following-node-name-re))
1736 (match-string 1)))
1737
a384cab3
JB
1738(defun Info-next ()
1739 "Go to the next node of this node."
1740 (interactive)
1741 (Info-goto-node (Info-extract-pointer "next")))
1742
1743(defun Info-prev ()
1744 "Go to the previous node of this node."
1745 (interactive)
1746 (Info-goto-node (Info-extract-pointer "prev[ious]*" "previous")))
1747
5dab2fb4
RS
1748(defun Info-up (&optional same-file)
1749 "Go to the superior node of this node.
1750If SAME-FILE is non-nil, do not move to a different Info file."
a384cab3 1751 (interactive)
8a7757f6
JL
1752 (let ((old-node Info-current-node)
1753 (old-file Info-current-file)
1754 (node (Info-extract-pointer "up")) p)
4fceda3c 1755 (and (or same-file (not (stringp Info-current-file)))
5dab2fb4
RS
1756 (string-match "^(" node)
1757 (error "Up node is in another Info file"))
8a7757f6
JL
1758 (Info-goto-node node)
1759 (setq p (point))
1760 (goto-char (point-min))
1761 (if (and (search-forward "\n* Menu:" nil t)
1762 (re-search-forward
1763 (if (string-equal old-node "Top")
1764 (concat "\n\\*[^:]+: +(" (file-name-nondirectory old-file) ")")
1765 (concat "\n\\* +\\(" (regexp-quote old-node)
1766 ":\\|[^:]+: +" (regexp-quote old-node) "\\)"))
1767 nil t))
1768 (beginning-of-line)
1769 (goto-char p)
1770 (Info-restore-point Info-history))))
a384cab3 1771
424b9d3e
JL
1772(defun Info-history-back ()
1773 "Go back in the history to the last node visited."
a384cab3
JB
1774 (interactive)
1775 (or Info-history
1776 (error "This is the first Info node you looked at"))
424b9d3e
JL
1777 (let ((history-forward
1778 (cons (list Info-current-file Info-current-node (point))
1779 Info-history-forward))
1780 filename nodename opoint)
a384cab3
JB
1781 (setq filename (car (car Info-history)))
1782 (setq nodename (car (cdr (car Info-history))))
1783 (setq opoint (car (cdr (cdr (car Info-history)))))
1784 (setq Info-history (cdr Info-history))
1785 (Info-find-node filename nodename)
1786 (setq Info-history (cdr Info-history))
424b9d3e
JL
1787 (setq Info-history-forward history-forward)
1788 (goto-char opoint)))
1789
1790(defalias 'Info-last 'Info-history-back)
1791
1792(defun Info-history-forward ()
1793 "Go forward in the history of visited nodes."
1794 (interactive)
1795 (or Info-history-forward
1796 (error "This is the last Info node you looked at"))
1797 (let ((history-forward (cdr Info-history-forward))
1798 filename nodename opoint)
1799 (setq filename (car (car Info-history-forward)))
1800 (setq nodename (car (cdr (car Info-history-forward))))
1801 (setq opoint (car (cdr (cdr (car Info-history-forward)))))
1802 (Info-find-node filename nodename)
1803 (setq Info-history-forward history-forward)
a384cab3
JB
1804 (goto-char opoint)))
1805
48e5b471 1806;;;###autoload
a384cab3
JB
1807(defun Info-directory ()
1808 "Go to the Info directory node."
1809 (interactive)
1810 (Info-find-node "dir" "top"))
1811\f
8a7757f6 1812(defun Info-history ()
89b064a9 1813 "Go to a node with a menu of visited nodes."
8a7757f6
JL
1814 (interactive)
1815 (let ((curr-file Info-current-file)
1816 (curr-node Info-current-node)
1817 p)
89b064a9
JL
1818 (with-current-buffer (get-buffer-create " *info-history*")
1819 (let ((inhibit-read-only t))
1820 (erase-buffer)
1821 (goto-char (point-min))
ea99d5c8 1822 (insert "\n\^_\nFile: history, Node: Top, Up: (dir)\n\n")
89b064a9
JL
1823 (insert "Recently Visited Nodes\n**********************\n\n")
1824 (insert "* Menu:\n\n")
1825 (let ((hl (delete '("history" "Top") Info-history-list)))
1826 (while hl
1827 (let ((file (nth 0 (car hl)))
1828 (node (nth 1 (car hl))))
1829 (if (and (string-equal file curr-file)
1830 (string-equal node curr-node))
1831 (setq p (point)))
1832 (insert "* " node ": (" (file-name-nondirectory file)
1833 ")" node ".\n"))
1834 (setq hl (cdr hl))))))
1835 (Info-find-node "history" "Top")
8a7757f6
JL
1836 (goto-char (or p (point-min)))))
1837
8a7757f6 1838(defun Info-toc ()
bc224645
JL
1839 "Go to a node with table of contents of the current Info file.
1840Table of contents is created from the tree structure of menus."
8a7757f6 1841 (interactive)
ea99d5c8
JL
1842 (let ((curr-file (substring-no-properties Info-current-file))
1843 (curr-node (substring-no-properties Info-current-node))
8a7757f6 1844 p)
89b064a9
JL
1845 (with-current-buffer (get-buffer-create " *info-toc*")
1846 (let ((inhibit-read-only t)
1847 (node-list (Info-build-toc curr-file)))
1848 (erase-buffer)
1849 (goto-char (point-min))
ea99d5c8 1850 (insert "\n\^_\nFile: toc, Node: Top, Up: (dir)\n\n")
89b064a9 1851 (insert "Table of Contents\n*****************\n\n")
ea99d5c8 1852 (insert "*Note Top: (" curr-file ")Top.\n")
89b064a9
JL
1853 (Info-insert-toc
1854 (nth 2 (assoc "Top" node-list)) ; get Top nodes
ea99d5c8 1855 node-list 0 curr-file))
89b064a9
JL
1856 (if (not (bobp))
1857 (let ((Info-hide-note-references 'hide)
1858 (Info-fontify-visited-nodes nil))
50163b00 1859 (Info-mode)
141b49f5 1860 (setq Info-current-file "toc" Info-current-node "Top")
ea99d5c8
JL
1861 (goto-char (point-min))
1862 (narrow-to-region (or (re-search-forward "\n[\^_\f]\n" nil t)
1863 (point-min))
1864 (point-max))
1865 (Info-fontify-node)
1866 (widen)))
89b064a9
JL
1867 (goto-char (point-min))
1868 (if (setq p (search-forward (concat "*Note " curr-node ":") nil t))
1869 (setq p (- p (length curr-node) 2))))
1870 (Info-find-node "toc" "Top")
8a7757f6
JL
1871 (goto-char (or p (point-min)))))
1872
89b064a9 1873(defun Info-insert-toc (nodes node-list level curr-file)
8a7757f6
JL
1874 "Insert table of contents with references to nodes."
1875 (let ((section "Top"))
1876 (while nodes
1877 (let ((node (assoc (car nodes) node-list)))
1878 (unless (member (nth 1 node) (list nil section))
1879 (insert (setq section (nth 1 node)) "\n"))
1880 (insert (make-string level ?\t))
89b064a9
JL
1881 (insert "*Note " (car nodes) ": (" curr-file ")" (car nodes) ".\n")
1882 (Info-insert-toc (nth 2 node) node-list (1+ level) curr-file)
8a7757f6
JL
1883 (setq nodes (cdr nodes))))))
1884
1885(defun Info-build-toc (file)
1886 "Build table of contents from menus of Info FILE and its subfiles."
8a7757f6 1887 (with-temp-buffer
ea99d5c8
JL
1888 (let* ((file (and (stringp file) (Info-find-file file)))
1889 (default-directory (or (and (stringp file)
1890 (file-name-directory file))
141b49f5 1891 default-directory))
ea99d5c8 1892 (main-file (and (stringp file) file))
141b49f5
JL
1893 (sections '(("Top" "Top")))
1894 nodes subfiles)
1895 (while (or main-file subfiles)
1896 (or main-file (message "Searching subfile %s..." (car subfiles)))
8a7757f6 1897 (erase-buffer)
141b49f5
JL
1898 (info-insert-file-contents (or main-file (car subfiles)))
1899 (goto-char (point-min))
8a7757f6
JL
1900 (while (and (search-forward "\n\^_\nFile:" nil 'move)
1901 (search-forward "Node: " nil 'move))
1902 (let ((nodename (substring-no-properties (Info-following-node-name)))
1903 (bound (- (or (save-excursion (search-forward "\n\^_" nil t))
1904 (point-max)) 2))
1905 (section "Top")
1906 menu-items)
141b49f5 1907 (when (and (not (Info-index-node nodename file))
8a7757f6
JL
1908 (re-search-forward "^\\* Menu:" bound t))
1909 (forward-line 1)
1910 (beginning-of-line)
1911 (setq bound (or (and (equal nodename "Top")
1912 (save-excursion
1913 (re-search-forward
1914 "^[ \t-]*The Detailed Node Listing" nil t)))
1915 bound))
1916 (while (< (point) bound)
1917 (cond
1918 ;; Menu item line
1919 ((looking-at "^\\* +[^:]+:")
1920 (beginning-of-line)
1921 (forward-char 2)
1922 (let ((menu-node-name (substring-no-properties
1923 (Info-extract-menu-node-name))))
1924 (setq menu-items (cons menu-node-name menu-items))
1925 (if (equal nodename "Top")
1926 (setq sections
1927 (cons (list menu-node-name section) sections)))))
1928 ;; Other non-empty strings in the Top node are section names
1929 ((and (equal nodename "Top")
1930 (looking-at "^\\([^ \t\n*=.-][^:\n]*\\)"))
1931 (setq section (match-string-no-properties 1))))
1932 (forward-line 1)
1933 (beginning-of-line)))
1934 (setq nodes (cons (list nodename
1935 (cadr (assoc nodename sections))
1936 (nreverse menu-items))
1937 nodes))
1938 (goto-char bound)))
141b49f5 1939 (if main-file
8a7757f6
JL
1940 (save-excursion
1941 (goto-char (point-min))
1942 (if (search-forward "\n\^_\nIndirect:" nil t)
1943 (let ((bound (save-excursion (search-forward "\n\^_" nil t))))
1944 (while (re-search-forward "^\\(.*\\): [0-9]+$" bound t)
1945 (setq subfiles (cons (match-string-no-properties 1)
1946 subfiles)))))
1947 (setq subfiles (nreverse subfiles)
141b49f5 1948 main-file nil))
8a7757f6
JL
1949 (setq subfiles (cdr subfiles))))
1950 (message "")
1951 (nreverse nodes))))
1952\f
1953(defun Info-follow-reference (footnotename &optional fork)
fdf4b680 1954 "Follow cross reference named FOOTNOTENAME to the node it refers to.
8a7757f6
JL
1955FOOTNOTENAME may be an abbreviation of the reference name.
1956If FORK is non-nil (interactively with a prefix arg), show the node in
1957a new info buffer. If FORK is a string, it is the name to use for the
1958new buffer."
a384cab3
JB
1959 (interactive
1960 (let ((completion-ignore-case t)
1bcedb3b 1961 (case-fold-search t)
67bc89ab 1962 completions default alt-default (start-point (point)) str i bol eol)
a384cab3 1963 (save-excursion
67bc89ab
RS
1964 ;; Store end and beginning of line.
1965 (end-of-line)
1966 (setq eol (point))
1967 (beginning-of-line)
1968 (setq bol (point))
1969
a384cab3 1970 (goto-char (point-min))
8a7757f6 1971 (while (re-search-forward "\\*note[ \n\t]+\\([^:]*\\):" nil t)
c60c12be 1972 (setq str (match-string-no-properties 1))
a384cab3
JB
1973 ;; See if this one should be the default.
1974 (and (null default)
1f179e27 1975 (<= (match-beginning 0) start-point)
a384cab3
JB
1976 (<= start-point (point))
1977 (setq default t))
67bc89ab
RS
1978 ;; See if this one should be the alternate default.
1979 (and (null alt-default)
1980 (and (<= bol (match-beginning 0))
1981 (<= (point) eol))
1982 (setq alt-default t))
a384cab3
JB
1983 (setq i 0)
1984 (while (setq i (string-match "[ \n\t]+" str i))
1985 (setq str (concat (substring str 0 i) " "
1986 (substring str (match-end 0))))
1987 (setq i (1+ i)))
1988 ;; Record as a completion and perhaps as default.
1989 (if (eq default t) (setq default str))
67bc89ab 1990 (if (eq alt-default t) (setq alt-default str))
ec6d29af 1991 ;; Don't add this string if it's a duplicate.
c60c12be
SM
1992 (or (assoc-string str completions t)
1993 (push str completions))))
67bc89ab
RS
1994 ;; If no good default was found, try an alternate.
1995 (or default
1996 (setq default alt-default))
1997 ;; If only one cross-reference found, then make it default.
1998 (if (eq (length completions) 1)
c60c12be 1999 (setq default (car completions)))
a384cab3 2000 (if completions
b0ebdfe5 2001 (let ((input (completing-read (if default
968b7671
MB
2002 (concat
2003 "Follow reference named: (default "
2004 default ") ")
b0ebdfe5
RS
2005 "Follow reference named: ")
2006 completions nil t)))
2007 (list (if (equal input "")
8a7757f6 2008 default input) current-prefix-arg))
a384cab3 2009 (error "No cross-references in this node"))))
ebf8f7e1
RS
2010
2011 (unless footnotename
2012 (error "No reference was specified"))
2013
dde7f979 2014 (let (target i (str (concat "\\*note " (regexp-quote footnotename)))
1bcedb3b 2015 (case-fold-search t))
a384cab3
JB
2016 (while (setq i (string-match " " str i))
2017 (setq str (concat (substring str 0 i) "[ \t\n]+" (substring str (1+ i))))
2018 (setq i (+ i 6)))
2019 (save-excursion
8a7757f6
JL
2020 ;; Move point to the beginning of reference if point is on reference
2021 (or (looking-at "\\*note[ \n\t]+")
2022 (and (looking-back "\\*note[ \n\t]+")
2023 (goto-char (match-beginning 0)))
2024 (if (and (save-excursion
2025 (goto-char (+ (point) 5)) ; skip a possible *note
2026 (re-search-backward "\\*note[ \n\t]+" nil t)
836bb478 2027 (looking-at str))
8a7757f6
JL
2028 (<= (point) (match-end 0)))
2029 (goto-char (match-beginning 0))))
2030 ;; Go to the reference closest to point
2031 (let ((next-ref (save-excursion (and (re-search-forward str nil t)
2032 (+ (match-beginning 0) 5))))
2033 (prev-ref (save-excursion (and (re-search-backward str nil t)
2034 (+ (match-beginning 0) 5)))))
2035 (goto-char (cond ((and next-ref prev-ref)
2036 (if (< (abs (- next-ref (point)))
2037 (abs (- prev-ref (point))))
2038 next-ref prev-ref))
2039 ((or next-ref prev-ref))
2040 ((error "No cross-reference named %s" footnotename))))
2041 (setq target (Info-extract-menu-node-name t))))
a384cab3
JB
2042 (while (setq i (string-match "[ \t\n]+" target i))
2043 (setq target (concat (substring target 0 i) " "
2044 (substring target (match-end 0))))
2045 (setq i (+ i 1)))
8a7757f6 2046 (Info-goto-node target fork)))
a384cab3 2047
e057da52
SM
2048(defconst Info-menu-entry-name-re "\\(?:[^:]\\|:[^:,.;() \t\n]\\)*"
2049 ;; We allow newline because this is also used in Info-follow-reference,
2050 ;; where the xref name might be wrapped over two lines.
c60c12be
SM
2051 "Regexp that matches a menu entry name upto but not including the colon.
2052Because of ambiguities, this should be concatenated with something like
2053`:' and `Info-following-node-name-re'.")
2054
836bb478 2055(defun Info-extract-menu-node-name (&optional multi-line index-node)
a384cab3 2056 (skip-chars-forward " \t\n")
c60c12be
SM
2057 (when (looking-at (concat Info-menu-entry-name-re ":\\(:\\|"
2058 (Info-following-node-name-re
836bb478
JL
2059 (cond
2060 (index-node "^,\t\n")
2061 (multi-line "^.,\t")
2062 (t "^.,\t\n")))
2063 "\\)"
2064 (if index-node
2065 "\\.\\(?:[ \t\n]+(line +\\([0-9]+\\))\\)?"
2066 "")))
2067 (if index-node
2068 (setq Info-point-loc
2069 (if (match-beginning 5)
2070 (string-to-number (match-string 5))
2071 (buffer-substring (match-beginning 0) (1- (match-beginning 1)))))
02c9f5cd 2072;;; Uncomment next line to use names of cross-references in non-index nodes:
836bb478
JL
2073;;; (setq Info-point-loc
2074;;; (buffer-substring (match-beginning 0) (1- (match-beginning 1))))
2075 )
c60c12be
SM
2076 (replace-regexp-in-string
2077 "[ \n]+" " "
2078 (or (match-string 2)
2079 ;; If the node name is the menu entry name (using `entry::').
2080 (buffer-substring (match-beginning 0) (1- (match-beginning 1)))))))
a384cab3 2081
e8adde3f 2082;; No one calls this.
9e5c2f50
RS
2083;;(defun Info-menu-item-sequence (list)
2084;; (while list
e8adde3f 2085;; (Info-menu (car list))
9e5c2f50 2086;; (setq list (cdr list))))
a384cab3 2087
6356e646 2088(defvar Info-complete-menu-buffer)
e4e8ee63 2089(defvar Info-complete-next-re nil)
141b49f5 2090(defvar Info-complete-nodes nil)
e4e8ee63 2091(defvar Info-complete-cache nil)
6356e646 2092
c60c12be
SM
2093(defconst Info-node-spec-re
2094 (concat (Info-following-node-name-re "^.,:") "[,:.]")
a1637357
SM
2095 "Regexp to match the text after a : until the terminating `.'.")
2096
e8adde3f 2097(defun Info-complete-menu-item (string predicate action)
2d12f5c2
SM
2098 ;; This uses two dynamically bound variables:
2099 ;; - `Info-complete-menu-buffer' which contains the buffer in which
2100 ;; is the menu of items we're trying to complete.
2101 ;; - `Info-complete-next-re' which, if non-nil, indicates that we should
2102 ;; also look for menu items in subsequent nodes as long as those
2103 ;; nodes' names match `Info-complete-next-re'. This feature is currently
141b49f5
JL
2104 ;; not used.
2105 ;; - `Info-complete-nodes' which, if non-nil, indicates that we should
2106 ;; also look for menu items in these nodes. This feature is currently
2d12f5c2 2107 ;; only used for completion in Info-index.
0defe758
LK
2108
2109 ;; Note that `Info-complete-menu-buffer' could be current already,
2110 ;; so we want to save point.
2111 (save-excursion
2112 (set-buffer Info-complete-menu-buffer)
e4e8ee63
SM
2113 (let ((completion-ignore-case t)
2114 (case-fold-search t)
2115 (orignode Info-current-node)
2116 nextnode)
2117 (goto-char (point-min))
2118 (search-forward "\n* Menu:")
2119 (if (not (memq action '(nil t)))
2120 (re-search-forward
2121 (concat "\n\\* +" (regexp-quote string) ":") nil t)
2122 (let ((pattern (concat "\n\\* +\\("
2123 (regexp-quote string)
c60c12be 2124 Info-menu-entry-name-re "\\):" Info-node-spec-re))
e4e8ee63
SM
2125 completions)
2126 ;; Check the cache.
2127 (if (and (equal (nth 0 Info-complete-cache) Info-current-file)
2128 (equal (nth 1 Info-complete-cache) Info-current-node)
2129 (equal (nth 2 Info-complete-cache) Info-complete-next-re)
141b49f5 2130 (equal (nth 5 Info-complete-cache) Info-complete-nodes)
e4e8ee63
SM
2131 (let ((prev (nth 3 Info-complete-cache)))
2132 (eq t (compare-strings string 0 (length prev)
2133 prev 0 nil t))))
2134 ;; We can reuse the previous list.
2135 (setq completions (nth 4 Info-complete-cache))
2136 ;; The cache can't be used.
2137 (while
2138 (progn
2139 (while (re-search-forward pattern nil t)
365d2503 2140 (push (match-string-no-properties 1)
e4e8ee63
SM
2141 completions))
2142 ;; Check subsequent nodes if applicable.
141b49f5
JL
2143 (or (and Info-complete-next-re
2144 (setq nextnode (Info-extract-pointer "next" t))
2145 (string-match Info-complete-next-re nextnode))
2146 (and Info-complete-nodes
2147 (setq Info-complete-nodes (cdr Info-complete-nodes)
2148 nextnode (car Info-complete-nodes)))))
e4e8ee63
SM
2149 (Info-goto-node nextnode))
2150 ;; Go back to the start node (for the next completion).
2151 (unless (equal Info-current-node orignode)
2152 (Info-goto-node orignode))
2153 ;; Update the cache.
271f4a9e
MB
2154 (set (make-local-variable 'Info-complete-cache)
2155 (list Info-current-file Info-current-node
141b49f5
JL
2156 Info-complete-next-re string completions
2157 Info-complete-nodes)))
e4e8ee63
SM
2158 (if action
2159 (all-completions string completions predicate)
2160 (try-completion string completions predicate)))))))
e8adde3f
RS
2161
2162
4fceda3c 2163(defun Info-menu (menu-item &optional fork)
dc414be1
EZ
2164 "Go to the node pointed to by the menu item named (or abbreviated) MENU-ITEM.
2165The menu item should one of those listed in the current node's menu.
2166Completion is allowed, and the default menu item is the one point is on.
7210b6f5
DL
2167If FORK is non-nil (interactively with a prefix arg), show the node in
2168a new info buffer. If FORK is a string, it is the name to use for the
2169new buffer."
a384cab3
JB
2170 (interactive
2171 (let ((completions '())
2172 ;; If point is within a menu item, use that item as the default
2173 (default nil)
2174 (p (point))
211d6309 2175 beg
760d5cb3
GM
2176 (last nil)
2177 (case-fold-search t))
a384cab3
JB
2178 (save-excursion
2179 (goto-char (point-min))
2180 (if (not (search-forward "\n* menu:" nil t))
2181 (error "No menu in this node"))
e8adde3f
RS
2182 (setq beg (point))
2183 (and (< (point) p)
2184 (save-excursion
2185 (goto-char p)
2186 (end-of-line)
00d32b3f
SM
2187 (if (re-search-backward (concat "\n\\* +\\("
2188 Info-menu-entry-name-re
2189 "\\):") beg t)
110d4e80 2190 (setq default (match-string-no-properties 1))))))
a384cab3
JB
2191 (let ((item nil))
2192 (while (null item)
e8adde3f
RS
2193 (setq item (let ((completion-ignore-case t)
2194 (Info-complete-menu-buffer (current-buffer)))
a384cab3
JB
2195 (completing-read (if default
2196 (format "Menu item (default %s): "
2197 default)
760d5cb3 2198 "Menu item: ")
e8adde3f 2199 'Info-complete-menu-item nil t)))
aea2a8da
JB
2200 ;; we rely on the fact that completing-read accepts an input
2201 ;; of "" even when the require-match argument is true and ""
2202 ;; is not a valid possibility
a384cab3
JB
2203 (if (string= item "")
2204 (if default
2205 (setq item default)
760d5cb3
GM
2206 ;; ask again
2207 (setq item nil))))
4fceda3c 2208 (list item current-prefix-arg))))
a384cab3
JB
2209 ;; there is a problem here in that if several menu items have the same
2210 ;; name you can only go to the node of the first with this command.
4fceda3c 2211 (Info-goto-node (Info-extract-menu-item menu-item) (if fork menu-item)))
399c88ad 2212
a384cab3
JB
2213(defun Info-extract-menu-item (menu-item)
2214 (setq menu-item (regexp-quote menu-item))
1bcedb3b
RS
2215 (let ((case-fold-search t))
2216 (save-excursion
760d5cb3
GM
2217 (let ((case-fold-search t))
2218 (goto-char (point-min))
2219 (or (search-forward "\n* menu:" nil t)
2220 (error "No menu in this node"))
2221 (or (re-search-forward (concat "\n\\* +" menu-item ":") nil t)
2222 (re-search-forward (concat "\n\\* +" menu-item) nil t)
2223 (error "No such item in menu"))
2224 (beginning-of-line)
2225 (forward-char 2)
141b49f5 2226 (Info-extract-menu-node-name nil (Info-index-node))))))
a384cab3
JB
2227
2228;; If COUNT is nil, use the last item in the menu.
2229(defun Info-extract-menu-counting (count)
1bcedb3b
RS
2230 (let ((case-fold-search t))
2231 (save-excursion
760d5cb3
GM
2232 (let ((case-fold-search t))
2233 (goto-char (point-min))
2234 (or (search-forward "\n* menu:" nil t)
2235 (error "No menu in this node"))
2236 (if count
2237 (or (search-forward "\n* " nil t count)
2238 (error "Too few items in menu"))
2239 (while (search-forward "\n* " nil t)
2240 nil))
141b49f5 2241 (Info-extract-menu-node-name nil (Info-index-node))))))
a384cab3 2242
e38e7367
RM
2243(defun Info-nth-menu-item ()
2244 "Go to the node of the Nth menu item.
2245N is the digit argument used to invoke this command."
a384cab3 2246 (interactive)
e38e7367
RM
2247 (Info-goto-node
2248 (Info-extract-menu-counting
2249 (- (aref (this-command-keys) (1- (length (this-command-keys)))) ?0))))
a384cab3
JB
2250
2251(defun Info-top-node ()
2252 "Go to the Top node of this file."
2253 (interactive)
2254 (Info-goto-node "Top"))
2255
2256(defun Info-final-node ()
2257 "Go to the final node in this file."
2258 (interactive)
2259 (Info-goto-node "Top")
760d5cb3
GM
2260 (let ((Info-history nil)
2261 (case-fold-search t))
a384cab3
JB
2262 ;; Go to the last node in the menu of Top.
2263 (Info-goto-node (Info-extract-menu-counting nil))
2264 ;; If the last node in the menu is not last in pointer structure,
399c88ad 2265 ;; move forward until we can't go any farther.
a384cab3
JB
2266 (while (Info-forward-node t t) nil)
2267 ;; Then keep moving down to last subnode, unless we reach an index.
141b49f5 2268 (while (and (not (Info-index-node))
a384cab3
JB
2269 (save-excursion (search-forward "\n* Menu:" nil t)))
2270 (Info-goto-node (Info-extract-menu-counting nil)))))
2271
2272(defun Info-forward-node (&optional not-down no-error)
2273 "Go forward one node, considering all nodes as forming one sequence."
2274 (interactive)
2275 (goto-char (point-min))
2276 (forward-line 1)
760d5cb3
GM
2277 (let ((case-fold-search t))
2278 ;; three possibilities, in order of priority:
2279 ;; 1. next node is in a menu in this node (but not in an index)
2280 ;; 2. next node is next at same level
2281 ;; 3. next node is up and next
2282 (cond ((and (not not-down)
2283 (save-excursion (search-forward "\n* menu:" nil t))
141b49f5 2284 (not (Info-index-node)))
760d5cb3
GM
2285 (Info-goto-node (Info-extract-menu-counting 1))
2286 t)
6af7040d 2287 ((save-excursion (search-backward "next:" nil t))
760d5cb3
GM
2288 (Info-next)
2289 t)
6af7040d 2290 ((and (save-excursion (search-backward "up:" nil t))
760d5cb3
GM
2291 ;; Use string-equal, not equal, to ignore text props.
2292 (not (string-equal (downcase (Info-extract-pointer "up"))
2293 "top")))
2294 (let ((old-node Info-current-node))
2295 (Info-up)
2296 (let (Info-history success)
2297 (unwind-protect
2298 (setq success (Info-forward-node t no-error))
2299 (or success (Info-goto-node old-node))))))
2300 (no-error nil)
2301 (t (error "No pointer forward from this node")))))
a384cab3
JB
2302
2303(defun Info-backward-node ()
2304 "Go backward one node, considering all nodes as forming one sequence."
2305 (interactive)
2306 (let ((prevnode (Info-extract-pointer "prev[ious]*" t))
760d5cb3
GM
2307 (upnode (Info-extract-pointer "up" t))
2308 (case-fold-search t))
a384cab3
JB
2309 (cond ((and upnode (string-match "(" upnode))
2310 (error "First node in file"))
2311 ((and upnode (or (null prevnode)
e90d1271
RS
2312 ;; Use string-equal, not equal,
2313 ;; to ignore text properties.
2314 (string-equal (downcase prevnode)
2315 (downcase upnode))))
a384cab3
JB
2316 (Info-up))
2317 (prevnode
2318 ;; If we move back at the same level,
2319 ;; go down to find the last subnode*.
2320 (Info-prev)
2321 (let (Info-history)
141b49f5 2322 (while (and (not (Info-index-node))
a384cab3
JB
2323 (save-excursion (search-forward "\n* Menu:" nil t)))
2324 (Info-goto-node (Info-extract-menu-counting nil)))))
2325 (t
2326 (error "No pointer backward from this node")))))
2327
2328(defun Info-exit ()
2329 "Exit Info by selecting some other buffer."
2330 (interactive)
552775bd
RS
2331 (if Info-standalone
2332 (save-buffers-kill-emacs)
4643e92f 2333 (quit-window)))
a384cab3 2334
253db917 2335(defun Info-next-menu-item ()
3a8803c0 2336 "Go to the node of the next menu item."
253db917 2337 (interactive)
fdf4b680 2338 ;; Bind this in case the user sets it to nil.
760d5cb3
GM
2339 (let* ((case-fold-search t)
2340 (node
2341 (save-excursion
2342 (forward-line -1)
2343 (search-forward "\n* menu:" nil t)
2344 (and (search-forward "\n* " nil t)
2345 (Info-extract-menu-node-name)))))
fadfb77f
RS
2346 (if node (Info-goto-node node)
2347 (error "No more items in menu"))))
253db917
ER
2348
2349(defun Info-last-menu-item ()
3a8803c0 2350 "Go to the node of the previous menu item."
253db917
ER
2351 (interactive)
2352 (save-excursion
2353 (forward-line 1)
fdf4b680 2354 ;; Bind this in case the user sets it to nil.
760d5cb3
GM
2355 (let* ((case-fold-search t)
2356 (beg (save-excursion
2357 (and (search-backward "\n* menu:" nil t)
2358 (point)))))
0a56332b
RS
2359 (or (and beg (search-backward "\n* " beg t))
2360 (error "No previous items in menu")))
2361 (Info-goto-node (save-excursion
2362 (goto-char (match-end 0))
2363 (Info-extract-menu-node-name)))))
253db917 2364
552775bd 2365(defmacro Info-no-error (&rest body)
253db917
ER
2366 (list 'condition-case nil (cons 'progn (append body '(t))) '(error nil)))
2367
2368(defun Info-next-preorder ()
3c9179ea
RS
2369 "Go to the next subnode or the next node, or go up a level."
2370 (interactive)
2371 (cond ((Info-no-error (Info-next-menu-item)))
2372 ((Info-no-error (Info-next)))
5dab2fb4 2373 ((Info-no-error (Info-up t))
ed690657
KH
2374 ;; Since we have already gone thru all the items in this menu,
2375 ;; go up to the end of this node.
b54d0f0e
RS
2376 (goto-char (point-max))
2377 ;; Since logically we are done with the node with that menu,
2378 ;; move on from it.
2379 (Info-next-preorder))
3c9179ea
RS
2380 (t
2381 (error "No more nodes"))))
253db917
ER
2382
2383(defun Info-last-preorder ()
2384 "Go to the last node, popping up a level if there is none."
2385 (interactive)
0a56332b
RS
2386 (cond ((Info-no-error
2387 (Info-last-menu-item)
2388 ;; If we go down a menu item, go to the end of the node
2389 ;; so we can scroll back through it.
ed690657 2390 (goto-char (point-max)))
b54d0f0e
RS
2391 ;; Keep going down, as long as there are nested menu nodes.
2392 (while (Info-no-error
2393 (Info-last-menu-item)
2394 ;; If we go down a menu item, go to the end of the node
2395 ;; so we can scroll back through it.
2396 (goto-char (point-max))))
ed690657 2397 (recenter -1))
c2def7a0
MB
2398 ((and (Info-no-error (Info-extract-pointer "prev"))
2399 (not (equal (Info-extract-pointer "up")
5dab2fb4
RS
2400 (Info-extract-pointer "prev"))))
2401 (Info-no-error (Info-prev))
ed690657 2402 (goto-char (point-max))
b54d0f0e
RS
2403 (while (Info-no-error
2404 (Info-last-menu-item)
2405 ;; If we go down a menu item, go to the end of the node
2406 ;; so we can scroll back through it.
2407 (goto-char (point-max))))
ed690657 2408 (recenter -1))
5dab2fb4 2409 ((Info-no-error (Info-up t))
ed690657 2410 (goto-char (point-min))
760d5cb3
GM
2411 (let ((case-fold-search t))
2412 (or (search-forward "\n* Menu:" nil t)
2413 (goto-char (point-max)))))
ed690657 2414 (t (error "No previous nodes"))))
253db917
ER
2415
2416(defun Info-scroll-up ()
3f32dc86 2417 "Scroll one screenful forward in Info, considering all nodes as one sequence.
280d11ed 2418Once you scroll far enough in a node that its menu appears on the screen
7a53d8c8
EZ
2419but after point, the next scroll moves into its first subnode, unless
2420`Info-scroll-prefer-subnodes' is nil.
280d11ed 2421
7a53d8c8
EZ
2422When you scroll past the end of a node, that goes to the next node if
2423`Info-scroll-prefer-subnodes' is non-nil and to the first subnode otherwise;
2424if this node has no successor, it moves to the parent node's successor,
2425and so on. If `Info-scroll-prefer-subnodes' is non-nil and point is inside
2426the menu of a node, it moves to subnode indicated by the following menu
2427item. (That case won't normally result from this command, but can happen
2428in other ways.)"
280d11ed 2429
253db917 2430 (interactive)
0a56332b
RS
2431 (if (or (< (window-start) (point-min))
2432 (> (window-start) (point-max)))
2433 (set-window-start (selected-window) (point)))
760d5cb3
GM
2434 (let* ((case-fold-search t)
2435 (virtual-end (save-excursion
2436 (goto-char (point-min))
7a53d8c8
EZ
2437 (if (and Info-scroll-prefer-subnodes
2438 (search-forward "\n* Menu:" nil t))
760d5cb3
GM
2439 (point)
2440 (point-max)))))
0a56332b
RS
2441 (if (or (< virtual-end (window-start))
2442 (pos-visible-in-window-p virtual-end))
7a53d8c8
EZ
2443 (cond
2444 (Info-scroll-prefer-subnodes (Info-next-preorder))
2445 ((Info-no-error (Info-goto-node (Info-extract-menu-counting 1))))
2446 (t (Info-next-preorder)))
0a56332b 2447 (scroll-up))))
253db917
ER
2448
2449(defun Info-scroll-down ()
3f32dc86 2450 "Scroll one screenful back in Info, considering all nodes as one sequence.
c2def7a0
MB
2451If point is within the menu of a node, and `Info-scroll-prefer-subnodes'
2452is non-nil, this goes to its last subnode. When you scroll past the
2453beginning of a node, that goes to the previous node or back up to the
2454parent node."
253db917 2455 (interactive)
0a56332b
RS
2456 (if (or (< (window-start) (point-min))
2457 (> (window-start) (point-max)))
2458 (set-window-start (selected-window) (point)))
760d5cb3
GM
2459 (let* ((case-fold-search t)
2460 (current-point (point))
c2def7a0
MB
2461 (virtual-end
2462 (and Info-scroll-prefer-subnodes
2463 (save-excursion
2464 (beginning-of-line)
2465 (setq current-point (point))
2466 (goto-char (point-min))
2467 (search-forward "\n* Menu:"
2468 current-point
2469 t)))))
3a8803c0 2470 (if (or virtual-end
7afe24e3 2471 (pos-visible-in-window-p (point-min) nil t))
0a56332b
RS
2472 (Info-last-preorder)
2473 (scroll-down))))
253db917 2474
56cda6f5 2475(defun Info-next-reference (&optional recur)
552775bd
RS
2476 "Move cursor to the next cross-reference or menu item in the node."
2477 (interactive)
8a7757f6 2478 (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tp://")
1bcedb3b
RS
2479 (old-pt (point))
2480 (case-fold-search t))
552775bd
RS
2481 (or (eobp) (forward-char 1))
2482 (or (re-search-forward pat nil t)
2483 (progn
2484 (goto-char (point-min))
2485 (or (re-search-forward pat nil t)
2486 (progn
2487 (goto-char old-pt)
2488 (error "No cross references in this node")))))
8a7757f6 2489 (goto-char (or (match-beginning 1) (match-beginning 0)))
552775bd 2490 (if (looking-at "\\* Menu:")
56cda6f5
RS
2491 (if recur
2492 (error "No cross references in this node")
2493 (Info-next-reference t)))))
552775bd 2494
56cda6f5 2495(defun Info-prev-reference (&optional recur)
552775bd
RS
2496 "Move cursor to the previous cross-reference or menu item in the node."
2497 (interactive)
8a7757f6 2498 (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tp://")
1bcedb3b
RS
2499 (old-pt (point))
2500 (case-fold-search t))
552775bd
RS
2501 (or (re-search-backward pat nil t)
2502 (progn
2503 (goto-char (point-max))
2504 (or (re-search-backward pat nil t)
2505 (progn
2506 (goto-char old-pt)
2507 (error "No cross references in this node")))))
8a7757f6 2508 (goto-char (or (match-beginning 1) (match-beginning 0)))
552775bd 2509 (if (looking-at "\\* Menu:")
56cda6f5
RS
2510 (if recur
2511 (error "No cross references in this node")
2512 (Info-prev-reference t)))))
141b49f5
JL
2513\f
2514(defvar Info-index-nodes nil
2515 "Alist of cached index node names of visited Info files.
2516Each element has the form (INFO-FILE INDEX-NODE-NAMES-LIST).")
2517
2518(defun Info-index-nodes (&optional file)
2519 "Return a list of names of all index nodes in Info FILE.
2520If FILE is omitted, it defaults to the current Info file.
50163b00
JL
2521First look in a list of cached index node names. Then scan Info
2522file and its subfiles for nodes with the index cookie. Then try
2523to find index nodes starting from the first node in the top level
2524menu whose name contains the word \"Index\", plus any immediately
2525following nodes whose names also contain the word \"Index\"."
141b49f5
JL
2526 (or file (setq file Info-current-file))
2527 (or (assoc file Info-index-nodes)
2528 ;; Skip virtual Info files
50163b00
JL
2529 (and (member file '("dir" "history" "toc" "apropos"))
2530 (setq Info-index-nodes (cons (cons file nil) Info-index-nodes)))
2531 (not (stringp file))
141b49f5
JL
2532 ;; Find nodes with index cookie
2533 (let* ((default-directory (or (and (stringp file)
2534 (file-name-directory
2535 (setq file (Info-find-file file))))
2536 default-directory))
50163b00
JL
2537 Info-history Info-history-list Info-fontify-maximum-menu-size
2538 (main-file file) subfiles nodes node)
2539 (condition-case nil
2540 (with-temp-buffer
2541 (while (or main-file subfiles)
2542 (erase-buffer)
2543 (info-insert-file-contents (or main-file (car subfiles)))
2544 (goto-char (point-min))
2545 (while (search-forward "\0\b[index\0\b]" nil 'move)
2546 (save-excursion
2547 (re-search-backward "^\^_")
2548 (search-forward "Node: ")
2549 (setq nodes (cons (Info-following-node-name) nodes))))
2550 (if main-file
2551 (save-excursion
2552 (goto-char (point-min))
2553 (if (search-forward "\n\^_\nIndirect:" nil t)
2554 (let ((bound (save-excursion (search-forward "\n\^_" nil t))))
2555 (while (re-search-forward "^\\(.*\\): [0-9]+$" bound t)
2556 (setq subfiles (cons (match-string-no-properties 1)
2557 subfiles)))))
2558 (setq subfiles (nreverse subfiles)
2559 main-file nil))
2560 (setq subfiles (cdr subfiles)))))
2561 (error nil))
141b49f5
JL
2562 (if nodes
2563 (setq nodes (nreverse nodes)
2564 Info-index-nodes (cons (cons file nodes) Info-index-nodes)))
2565 nodes)
50163b00
JL
2566 ;; Find nodes with the word "Index" in the node name
2567 (let ((case-fold-search t)
2568 Info-history Info-history-list Info-fontify-maximum-menu-size
2569 nodes node)
2570 (condition-case nil
2571 (with-temp-buffer
2572 (Info-mode)
2573 (Info-find-node file "Top")
2574 (when (and (search-forward "\n* menu:" nil t)
2575 (re-search-forward "\n\\* \\(.*\\<Index\\>\\)" nil t))
2576 (goto-char (match-beginning 1))
2577 (setq nodes (list (Info-extract-menu-node-name)))
2578 (Info-goto-node (car nodes))
2579 (while (and (setq node (Info-extract-pointer "next" t))
2580 (string-match "\\<Index\\>" node))
2581 (setq nodes (cons node nodes))
2582 (Info-goto-node node))))
2583 (error nil))
141b49f5
JL
2584 (if nodes
2585 (setq nodes (nreverse nodes)
2586 Info-index-nodes (cons (cons file nodes) Info-index-nodes)))
2587 nodes)
50163b00
JL
2588 ;; If file has no index nodes, still add it to the cache
2589 (setq Info-index-nodes (cons (cons file nil) Info-index-nodes)))
141b49f5
JL
2590 (cdr (assoc file Info-index-nodes)))
2591
2592(defun Info-index-node (&optional node file)
2593 "Return non-nil value if NODE is an index node.
2594If NODE is nil, check the current Info node.
2595If FILE is nil, check the current Info file."
50163b00
JL
2596 (if (or (and node (not (equal node Info-current-node)))
2597 (assoc (or file Info-current-file) Info-index-nodes))
2598 (member (or node Info-current-node) (Info-index-nodes file))
2599 ;; Don't search all index nodes if request is only for the current node
2600 ;; and file is not in the cache of index nodes
2601 (or
2602 (save-match-data
2603 (string-match "\\<Index\\>" (or node Info-current-node "")))
2604 (save-excursion
2605 (goto-char (+ (or (save-excursion
2606 (search-backward "\n\^_" nil t))
2607 (point-min)) 2))
2608 (search-forward "\0\b[index\0\b]"
2609 (or (save-excursion
2610 (search-forward "\n\^_" nil t))
2611 (point-max)) t)))))
552775bd 2612
e4e8ee63 2613(defun Info-goto-index ()
141b49f5
JL
2614 "Go to the first index node."
2615 (let ((node (car (Info-index-nodes))))
2616 (or node (error "No index"))
2617 (Info-goto-node node)))
e4e8ee63 2618
201bc1bd 2619;;;###autoload
1143a6b0 2620(defun Info-index (topic)
fdf4b680 2621 "Look up a string TOPIC in the index for this file.
1143a6b0
ER
2622If there are no exact matches to the specified topic, this chooses
2623the first match which is a case-insensitive substring of a topic.
89b064a9 2624Use the \\<Info-mode-map>\\[Info-index-next] command to see the other matches.
1143a6b0 2625Give a blank topic name to go to the Index node itself."
e4e8ee63
SM
2626 (interactive
2627 (list
2628 (let ((Info-complete-menu-buffer (clone-buffer))
141b49f5
JL
2629 (Info-complete-nodes (Info-index-nodes))
2630 (Info-history-list nil))
aebd1760
RS
2631 (if (equal Info-current-file "dir")
2632 (error "The Info directory node has no index; use m to select a manual"))
e4e8ee63
SM
2633 (unwind-protect
2634 (with-current-buffer Info-complete-menu-buffer
2635 (Info-goto-index)
2636 (completing-read "Index topic: " 'Info-complete-menu-item))
2637 (kill-buffer Info-complete-menu-buffer)))))
aebd1760
RS
2638 (if (equal Info-current-file "dir")
2639 (error "The Info directory node has no index; use m to select a manual"))
1143a6b0 2640 (let ((orignode Info-current-node)
836bb478 2641 (pattern (format "\n\\* +\\([^\n]*%s[^\n]*\\):[ \t]+\\([^\n]*\\)\\.\\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?"
1143a6b0 2642 (regexp-quote topic)))
141b49f5
JL
2643 node (nodes (Info-index-nodes))
2644 (ohist-list Info-history-list)
1bcedb3b 2645 (case-fold-search t))
e700ec12 2646 (Info-goto-index)
1143a6b0
ER
2647 (or (equal topic "")
2648 (let ((matches nil)
2649 (exact nil)
e700ec12
SM
2650 ;; We bind Info-history to nil for internal node-switches so
2651 ;; that we don't put junk in the history. In the first
2652 ;; Info-goto-index call, above, we do update the history
2653 ;; because that is what the user's previous node choice into it.
c696ac76 2654 (Info-history nil)
1143a6b0
ER
2655 found)
2656 (while
2657 (progn
2658 (goto-char (point-min))
2659 (while (re-search-forward pattern nil t)
983dfbf8
SM
2660 (push (list (match-string-no-properties 1)
2661 (match-string-no-properties 2)
2662 Info-current-node
3a8803c0
JPW
2663 (string-to-number (concat "0"
2664 (match-string 3))))
983dfbf8 2665 matches))
141b49f5 2666 (setq nodes (cdr nodes) node (car nodes)))
1143a6b0
ER
2667 (Info-goto-node node))
2668 (or matches
2669 (progn
81e14cb2 2670 (Info-goto-node orignode)
920bdaab 2671 (error "No `%s' in index" topic)))
1143a6b0
ER
2672 ;; Here it is a feature that assoc is case-sensitive.
2673 (while (setq found (assoc topic matches))
2674 (setq exact (cons found exact)
2675 matches (delq found matches)))
141b49f5 2676 (setq Info-history-list ohist-list)
1143a6b0
ER
2677 (setq Info-index-alternatives (nconc exact (nreverse matches)))
2678 (Info-index-next 0)))))
2679
2680(defun Info-index-next (num)
89b064a9 2681 "Go to the next matching index item from the last \\<Info-mode-map>\\[Info-index] command."
1143a6b0
ER
2682 (interactive "p")
2683 (or Info-index-alternatives
882e61bf 2684 (error "No previous `i' command"))
1143a6b0
ER
2685 (while (< num 0)
2686 (setq num (+ num (length Info-index-alternatives))))
2687 (while (> num 0)
2688 (setq Info-index-alternatives
2689 (nconc (cdr Info-index-alternatives)
2690 (list (car Info-index-alternatives)))
2691 num (1- num)))
2692 (Info-goto-node (nth 1 (car Info-index-alternatives)))
2693 (if (> (nth 3 (car Info-index-alternatives)) 0)
836bb478 2694 (forward-line (1- (nth 3 (car Info-index-alternatives))))
760d5cb3 2695 (forward-line 3) ; don't search in headers
1143a6b0 2696 (let ((name (car (car Info-index-alternatives))))
920bdaab
RS
2697 (Info-find-index-name name)))
2698 (message "Found `%s' in %s. %s"
1143a6b0
ER
2699 (car (car Info-index-alternatives))
2700 (nth 2 (car Info-index-alternatives))
2701 (if (cdr Info-index-alternatives)
c702ed73 2702 "(`,' tries to find next)"
1143a6b0
ER
2703 "(Only match)")))
2704
920bdaab
RS
2705(defun Info-find-index-name (name)
2706 "Move point to the place within the current node where NAME is defined."
1bcedb3b
RS
2707 (let ((case-fold-search t))
2708 (if (or (re-search-forward (format
2709 "[a-zA-Z]+: %s\\( \\|$\\)"
2710 (regexp-quote name)) nil t)
d31c6ecf
RS
2711 ;; Find a function definition with a return type.
2712 (re-search-forward (format
f2fc5b0a 2713 "[a-zA-Z]+: [a-zA-Z0-9_ *&]+ %s\\( \\|$\\)"
d31c6ecf 2714 (regexp-quote name)) nil t)
1bcedb3b
RS
2715 (search-forward (format "`%s'" name) nil t)
2716 (and (string-match "\\`.*\\( (.*)\\)\\'" name)
2717 (search-forward
2718 (format "`%s'" (substring name 0 (match-beginning 1)))
2719 nil t))
bbe054f7
EZ
2720 (search-forward name nil t)
2721 ;; Try again without the " <1>" makeinfo can append
2722 (and (string-match "\\`\\(.*\\) <[0-9]+>\\'" name)
2723 (Info-find-index-name (match-string 1 name))))
2724 (progn (beginning-of-line) t) ;; non-nil for recursive call
1bcedb3b 2725 (goto-char (point-min)))))
920bdaab 2726
ab2f22ad
JH
2727;;;###autoload
2728(defun info-apropos (string)
2729 "Grovel indices of all known Info files on your system for STRING.
2730Build a menu of the possible matches."
2731 (interactive "sIndex apropos: ")
2732 (unless (string= string "")
836bb478 2733 (let ((pattern (format "\n\\* +\\([^\n]*%s[^\n]*\\):[ \t]+\\([^\n]+\\)\\.\\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?"
ab2f22ad
JH
2734 (regexp-quote string)))
2735 (ohist Info-history)
37600b6d 2736 (ohist-list Info-history-list)
ab2f22ad
JH
2737 (current-node Info-current-node)
2738 (current-file Info-current-file)
141b49f5
JL
2739 manuals matches node nodes)
2740 (let ((Info-fontify-maximum-menu-size nil))
ab2f22ad
JH
2741 (Info-directory)
2742 (message "Searching indices...")
2743 (goto-char (point-min))
2744 (re-search-forward "\\* Menu: *\n" nil t)
7b30b20c 2745 (while (re-search-forward "\\*.*: *(\\([^)]+\\))" nil t)
ab2f22ad
JH
2746 (add-to-list 'manuals (match-string 1)))
2747 (dolist (manual manuals)
2748 (message "Searching %s" manual)
141b49f5
JL
2749 (if (setq nodes (Info-index-nodes (Info-find-file manual)))
2750 (condition-case nil
2751 (save-excursion
2752 (Info-find-node manual (car nodes))
2753 (while
2754 (progn
2755 (goto-char (point-min))
2756 (while (re-search-forward pattern nil t)
2757 (add-to-list 'matches
2758 (list manual
2759 (match-string-no-properties 1)
2760 (match-string-no-properties 2)
2761 (match-string-no-properties 3))))
2762 (setq nodes (cdr nodes) node (car nodes)))
2763 (Info-goto-node node)))
2764 (error nil)))))
ab2f22ad 2765 (Info-goto-node (concat "(" current-file ")" current-node))
37600b6d
JH
2766 (setq Info-history ohist
2767 Info-history-list ohist-list)
ab2f22ad
JH
2768 (message "Searching indices...done")
2769 (if (null matches)
2770 (message "No matches found")
2771 (with-current-buffer (get-buffer-create " *info-apropos*")
2772 (erase-buffer)
141b49f5 2773 (insert "\n\^_\nFile: apropos, Node: Index, Up: (dir)\n")
ab2f22ad
JH
2774 (insert "* Menu: \nNodes whose indices contain \"" string "\"\n\n")
2775 (dolist (entry matches)
5242942f
JH
2776 (insert
2777 (format "* %-38s (%s)%s.%s\n"
2778 (concat (nth 1 entry) " [" (nth 0 entry) "]:")
2779 (nth 0 entry)
2780 (nth 2 entry)
2781 (if (nth 3 entry)
2782 (concat " (line " (nth 3 entry) ")")
2783 "")))))
2784 (Info-find-node "apropos" "Index")
82f1aca9 2785 (setq Info-complete-cache nil)))))
ab2f22ad 2786
a384cab3
JB
2787(defun Info-undefined ()
2788 "Make command be undefined in Info."
2789 (interactive)
2790 (ding))
2791
2792(defun Info-help ()
2793 "Enter the Info tutorial."
2794 (interactive)
2795 (delete-other-windows)
2796 (Info-find-node "info"
2797 (if (< (window-height) 23)
2798 "Help-Small-Screen"
2799 "Help")))
2800
2801(defun Info-summary ()
2802 "Display a brief summary of all Info commands."
2803 (interactive)
2804 (save-window-excursion
2805 (switch-to-buffer "*Help*")
881c84c7 2806 (setq buffer-read-only nil)
a384cab3
JB
2807 (erase-buffer)
2808 (insert (documentation 'Info-mode))
9d29f94c 2809 (help-mode)
a384cab3
JB
2810 (goto-char (point-min))
2811 (let (ch flag)
2812 (while (progn (setq flag (not (pos-visible-in-window-p (point-max))))
2813 (message (if flag "Type Space to see more"
2814 "Type Space to return to Info"))
1614c867 2815 (if (not (eq ?\ (setq ch (read-event))))
dbc4e1c1 2816 (progn (setq unread-command-events (list ch)) nil)
a384cab3 2817 flag))
552775bd
RS
2818 (scroll-up)))
2819 (bury-buffer "*Help*")))
a384cab3
JB
2820\f
2821(defun Info-get-token (pos start all &optional errorstring)
fdf4b680 2822 "Return the token around POS.
a384cab3
JB
2823POS must be somewhere inside the token
2824START is a regular expression which will match the
2825 beginning of the tokens delimited string
2826ALL is a regular expression with a single
90a715f0 2827 parenthesized subpattern which is the token to be
fdf4b680 2828 returned. E.g. '{\(.*\)}' would return any string
a384cab3 2829 enclosed in braces around POS.
fdf4b680 2830ERRORSTRING optional fourth argument, controls action on no match
a384cab3
JB
2831 nil: return nil
2832 t: beep
2833 a string: signal an error, using that string."
1bcedb3b
RS
2834 (let ((case-fold-search t))
2835 (save-excursion
2836 (goto-char pos)
2837 ;; First look for a match for START that goes across POS.
2838 (while (and (not (bobp)) (> (point) (- pos (length start)))
2839 (not (looking-at start)))
2840 (forward-char -1))
2841 ;; If we did not find one, search back for START
2842 ;; (this finds only matches that end at or before POS).
2843 (or (looking-at start)
2844 (progn
2845 (goto-char pos)
2846 (re-search-backward start (max (point-min) (- pos 200)) 'yes)))
2847 (let (found)
2848 (while (and (re-search-forward all (min (point-max) (+ pos 200)) 'yes)
2849 (not (setq found (and (<= (match-beginning 0) pos)
2850 (> (match-end 0) pos))))))
2851 (if (and found (<= (match-beginning 0) pos)
2852 (> (match-end 0) pos))
110d4e80 2853 (match-string-no-properties 1)
1bcedb3b
RS
2854 (cond ((null errorstring)
2855 nil)
2856 ((eq errorstring t)
2857 (beep)
2858 nil)
2859 (t
2860 (error "No %s around position %d" errorstring pos))))))))
a384cab3 2861
981947af 2862(defun Info-mouse-follow-nearest-node (click)
f9969361
RS
2863 "\\<Info-mode-map>Follow a node reference near point.
2864Like \\[Info-menu], \\[Info-follow-reference], \\[Info-next], \\[Info-prev] or \\[Info-up] command, depending on where you click.
981947af 2865At end of the node's text, moves to the next node, or up if none."
f9969361 2866 (interactive "e")
d1268e52 2867 (mouse-set-point click)
981947af
KH
2868 (and (not (Info-try-follow-nearest-node))
2869 (save-excursion (forward-line 1) (eobp))
ed690657 2870 (Info-next-preorder)))
981947af 2871
8a7757f6 2872(defun Info-follow-nearest-node (&optional fork)
d0b560c6
JB
2873 "Follow a node reference near point.
2874If point is on a reference, follow that reference. Otherwise,
2875if point is in a menu item description, follow that menu item."
8a7757f6
JL
2876 (interactive "P")
2877 (or (Info-try-follow-nearest-node fork)
d0b560c6
JB
2878 (when (save-excursion
2879 (search-backward "\n* menu:" nil t))
2880 (save-excursion
2881 (beginning-of-line)
2882 (while (not (or (bobp) (looking-at "[^ \t]\\|[ \t]*$")))
2883 (beginning-of-line 0))
2884 (when (looking-at "\\* +\\([^\t\n]*\\):")
2885 (Info-goto-node
8a7757f6 2886 (Info-extract-menu-item (match-string-no-properties 1)) fork)
d0b560c6
JB
2887 t)))
2888 (error "Point neither on reference nor in menu item description")))
981947af
KH
2889
2890;; Common subroutine.
8a7757f6 2891(defun Info-try-follow-nearest-node (&optional fork)
981947af 2892 "Follow a node reference near point. Return non-nil if successful."
6af7040d 2893 (let (node)
a384cab3 2894 (cond
6b3600ff 2895 ((Info-get-token (point) "[hf]t?tp://" "[hf]t?tp://\\([^ \t\n\"`({<>})']+\\)")
8a7757f6
JL
2896 (setq node t)
2897 (browse-url (browse-url-url-at-point)))
2898 ((setq node (Info-get-token (point) "\\*note[ \n\t]+"
2899 "\\*note[ \n\t]+\\([^:]*\\):\\(:\\|[ \n\t]*(\\)?"))
8a7757f6 2900 (Info-follow-reference node fork))
505b68d5 2901 ;; menu item: node name
b37daea4 2902 ((setq node (Info-get-token (point) "\\* +" "\\* +\\([^:]*\\)::"))
8a7757f6 2903 (Info-goto-node node fork))
836bb478 2904 ;; menu item: node name or index entry
505b68d5 2905 ((Info-get-token (point) "\\* +" "\\* +\\(.*\\): ")
631ba13e
RS
2906 (beginning-of-line)
2907 (forward-char 2)
141b49f5 2908 (setq node (Info-extract-menu-node-name nil (Info-index-node)))
8a7757f6 2909 (Info-goto-node node fork))
bc2ada62 2910 ((setq node (Info-get-token (point) "Up: " "Up: \\([^,\n\t]*\\)"))
8a7757f6 2911 (Info-goto-node node fork))
bc2ada62 2912 ((setq node (Info-get-token (point) "Next: " "Next: \\([^,\n\t]*\\)"))
8a7757f6 2913 (Info-goto-node node fork))
bc2ada62 2914 ((setq node (Info-get-token (point) "File: " "File: \\([^,\n\t]*\\)"))
8a7757f6 2915 (Info-goto-node "Top" fork))
bc2ada62 2916 ((setq node (Info-get-token (point) "Prev: " "Prev: \\([^,\n\t]*\\)"))
8a7757f6 2917 (Info-goto-node node fork)))
981947af 2918 node))
a384cab3
JB
2919\f
2920(defvar Info-mode-map nil
2921 "Keymap containing Info commands.")
2922(if Info-mode-map
2923 nil
2924 (setq Info-mode-map (make-keymap))
2925 (suppress-keymap Info-mode-map)
2926 (define-key Info-mode-map "." 'beginning-of-buffer)
253db917 2927 (define-key Info-mode-map " " 'Info-scroll-up)
981947af 2928 (define-key Info-mode-map "\C-m" 'Info-follow-nearest-node)
552775bd 2929 (define-key Info-mode-map "\t" 'Info-next-reference)
508f27a0
RS
2930 (define-key Info-mode-map [(shift tab)] 'Info-prev-reference)
2931 (define-key Info-mode-map [backtab] 'Info-prev-reference)
e38e7367
RM
2932 (define-key Info-mode-map "1" 'Info-nth-menu-item)
2933 (define-key Info-mode-map "2" 'Info-nth-menu-item)
2934 (define-key Info-mode-map "3" 'Info-nth-menu-item)
2935 (define-key Info-mode-map "4" 'Info-nth-menu-item)
2936 (define-key Info-mode-map "5" 'Info-nth-menu-item)
2937 (define-key Info-mode-map "6" 'Info-nth-menu-item)
2938 (define-key Info-mode-map "7" 'Info-nth-menu-item)
2939 (define-key Info-mode-map "8" 'Info-nth-menu-item)
2940 (define-key Info-mode-map "9" 'Info-nth-menu-item)
82a4c008 2941 (define-key Info-mode-map "0" 'undefined)
a384cab3
JB
2942 (define-key Info-mode-map "?" 'Info-summary)
2943 (define-key Info-mode-map "]" 'Info-forward-node)
2944 (define-key Info-mode-map "[" 'Info-backward-node)
2945 (define-key Info-mode-map "<" 'Info-top-node)
2946 (define-key Info-mode-map ">" 'Info-final-node)
2947 (define-key Info-mode-map "b" 'beginning-of-buffer)
2ad63128 2948 (define-key Info-mode-map "c" 'Info-copy-current-node-name)
a384cab3
JB
2949 (define-key Info-mode-map "d" 'Info-directory)
2950 (define-key Info-mode-map "e" 'Info-edit)
2951 (define-key Info-mode-map "f" 'Info-follow-reference)
2952 (define-key Info-mode-map "g" 'Info-goto-node)
2953 (define-key Info-mode-map "h" 'Info-help)
1143a6b0 2954 (define-key Info-mode-map "i" 'Info-index)
424b9d3e 2955 (define-key Info-mode-map "l" 'Info-history-back)
bc224645 2956 (define-key Info-mode-map "L" 'Info-history)
a384cab3
JB
2957 (define-key Info-mode-map "m" 'Info-menu)
2958 (define-key Info-mode-map "n" 'Info-next)
2959 (define-key Info-mode-map "p" 'Info-prev)
2960 (define-key Info-mode-map "q" 'Info-exit)
424b9d3e 2961 (define-key Info-mode-map "r" 'Info-history-forward)
a384cab3 2962 (define-key Info-mode-map "s" 'Info-search)
50163b00 2963 (define-key Info-mode-map "S" 'Info-search-case-sensitively)
47fc33ca
RS
2964 ;; For consistency with Rmail.
2965 (define-key Info-mode-map "\M-s" 'Info-search)
4fceda3c 2966 (define-key Info-mode-map "\M-n" 'clone-buffer)
db0c9809 2967 (define-key Info-mode-map "t" 'Info-top-node)
bc224645 2968 (define-key Info-mode-map "T" 'Info-toc)
a384cab3 2969 (define-key Info-mode-map "u" 'Info-up)
50163b00
JL
2970 ;; For consistency with dired-copy-filename-as-kill.
2971 (define-key Info-mode-map "w" 'Info-copy-current-node-name)
1143a6b0 2972 (define-key Info-mode-map "," 'Info-index-next)
803eaf50 2973 (define-key Info-mode-map "\177" 'Info-scroll-down)
981947af 2974 (define-key Info-mode-map [mouse-2] 'Info-mouse-follow-nearest-node)
3c1498ac 2975 (define-key Info-mode-map [follow-link] 'mouse-face)
aea2a8da 2976 )
75a209d4
RS
2977
2978(defun Info-check-pointer (item)
fdf4b680 2979 "Non-nil if ITEM is present in this node."
75a209d4
RS
2980 (condition-case nil
2981 (Info-extract-pointer item)
2982 (error nil)))
2983
d4af987a
DL
2984(easy-menu-define
2985 Info-mode-menu Info-mode-map
2986 "Menu for info files."
2987 '("Info"
2988 ["Up" Info-up :active (Info-check-pointer "up")
2989 :help "Go up in the Info tree"]
2990 ["Next" Info-next :active (Info-check-pointer "next")
2991 :help "Go to the next node"]
2992 ["Previous" Info-prev :active (Info-check-pointer "prev[ious]*")
2993 :help "Go to the previous node"]
2994 ["Backward" Info-backward-node
2995 :help "Go backward one node, considering all as a sequence"]
2996 ["Forward" Info-forward-node
2997 :help "Go forward one node, considering all as a sequence"]
7210b6f5
DL
2998 ["Beginning" beginning-of-buffer
2999 :help "Go to beginning of this node"]
d4af987a
DL
3000 ["Top" Info-top-node
3001 :help "Go to top node of file"]
3002 ["Final Node" Info-final-node
3003 :help "Go to final node in this file"]
3004 ("Menu Item" ["You should never see this" report-emacs-bug t])
3005 ("Reference" ["You should never see this" report-emacs-bug t])
3006 ["Search..." Info-search
3007 :help "Search for regular expression in this Info file"]
8a7757f6
JL
3008 ["Search Case-Sensitively..." Info-search-case-sensitively
3009 :help "Search for regular expression case sensitively"]
3010 ["Search Next" Info-search-next
3011 :help "Search for another occurrence of regular expression"]
d1268e52 3012 ["Go to Node..." Info-goto-node
d4af987a 3013 :help "Go to a named node"]
424b9d3e
JL
3014 ["Back in history" Info-history-back :active Info-history
3015 :help "Go back in history to the last node you were at"]
3016 ["Forward in history" Info-history-forward :active Info-history-forward
3017 :help "Go forward in history"]
8a7757f6 3018 ["History" Info-history :active Info-history-list
bc224645 3019 :help "Go to menu of visited nodes"]
8a7757f6 3020 ["Table of Contents" Info-toc
bc224645 3021 :help "Go to table of contents"]
d4af987a
DL
3022 ("Index..."
3023 ["Lookup a String" Info-index
3024 :help "Look for a string in the index items"]
3025 ["Next Matching Item" Info-index-next
ab2f22ad
JH
3026 :help "Look for another occurrence of previous item"]
3027 ["Lookup a string in all indices" info-apropos
3028 :help "Look for a string in the indices of all manuals"])
7210b6f5
DL
3029 ["Edit" Info-edit :help "Edit contents of this node"
3030 :active Info-enable-edit]
126f5d4d
MB
3031 ["Copy Node Name" Info-copy-current-node-name
3032 :help "Copy the name of the current node into the kill ring"]
8a7757f6
JL
3033 ["Clone Info buffer" clone-buffer
3034 :help "Create a twin copy of the current Info buffer."]
ec03f31e 3035 ["Exit" Info-exit :help "Stop reading Info"]))
75a209d4 3036
7210b6f5
DL
3037
3038(defvar info-tool-bar-map
3039 (if (display-graphic-p)
df6aed99
RS
3040 (let ((map (make-sparse-keymap)))
3041 (tool-bar-local-item-from-menu 'Info-exit "close" map Info-mode-map)
3042 (tool-bar-local-item-from-menu 'Info-prev "left_arrow" map Info-mode-map)
3043 (tool-bar-local-item-from-menu 'Info-next "right_arrow" map Info-mode-map)
3044 (tool-bar-local-item-from-menu 'Info-up "up_arrow" map Info-mode-map)
424b9d3e
JL
3045 (tool-bar-local-item-from-menu 'Info-history-back "back_arrow" map Info-mode-map)
3046 (tool-bar-local-item-from-menu 'Info-history-forward "fwd_arrow" map Info-mode-map)
df6aed99
RS
3047 (tool-bar-local-item-from-menu 'Info-top-node "home" map Info-mode-map)
3048 (tool-bar-local-item-from-menu 'Info-index "index" map Info-mode-map)
3049 (tool-bar-local-item-from-menu 'Info-goto-node "jump_to" map Info-mode-map)
3050 (tool-bar-local-item-from-menu 'Info-search "search" map Info-mode-map)
3051 map)))
7210b6f5 3052
75a209d4
RS
3053(defvar Info-menu-last-node nil)
3054;; Last node the menu was created for.
6d2c8e3e 3055;; Value is a list, (FILE-NAME NODE-NAME).
75a209d4
RS
3056
3057(defun Info-menu-update ()
fdf4b680 3058 "Update the Info menu for the current node."
75a209d4
RS
3059 (condition-case nil
3060 (if (or (not (eq major-mode 'Info-mode))
6d2c8e3e
RS
3061 (equal (list Info-current-file Info-current-node)
3062 Info-menu-last-node))
75a209d4
RS
3063 ()
3064 ;; Update menu menu.
3065 (let* ((Info-complete-menu-buffer (current-buffer))
3066 (items (nreverse (condition-case nil
e4e8ee63 3067 (Info-complete-menu-item "" nil t)
75a209d4 3068 (error nil))))
399c88ad 3069 entries current
75a209d4
RS
3070 (number 0))
3071 (while (and items (< number 9))
3072 (setq current (car items)
3073 items (cdr items)
3074 number (1+ number))
399c88ad 3075 (setq entries (cons `[,current
75a209d4
RS
3076 (Info-menu ,current)
3077 :keys ,(format "%d" number)]
3078 entries)))
3079 (if items
3080 (setq entries (cons ["Other..." Info-menu t] entries)))
3081 (or entries
5d5845dc 3082 (setq entries (list ["No menu" nil nil] nil :active)))
d4af987a 3083 (easy-menu-change '("Info") "Menu Item" (nreverse entries)))
75a209d4
RS
3084 ;; Update reference menu. Code stolen from `Info-follow-reference'.
3085 (let ((items nil)
399c88ad 3086 str i entries current
1bcedb3b
RS
3087 (number 0)
3088 (case-fold-search t))
75a209d4
RS
3089 (save-excursion
3090 (goto-char (point-min))
8a7757f6 3091 (while (re-search-forward "\\*note[ \n\t]+\\([^:]*\\):" nil t)
c60c12be 3092 (setq str (match-string 1))
75a209d4
RS
3093 (setq i 0)
3094 (while (setq i (string-match "[ \n\t]+" str i))
3095 (setq str (concat (substring str 0 i) " "
3096 (substring str (match-end 0))))
3097 (setq i (1+ i)))
3098 (setq items
3099 (cons str items))))
3100 (while (and items (< number 9))
3101 (setq current (car items)
3102 items (cdr items)
3103 number (1+ number))
399c88ad 3104 (setq entries (cons `[,current
75a209d4
RS
3105 (Info-follow-reference ,current)
3106 t]
3107 entries)))
3108 (if items
3109 (setq entries (cons ["Other..." Info-follow-reference t]
3110 entries)))
3111 (or entries
5d5845dc 3112 (setq entries (list ["No references" nil nil] nil :active)))
75a209d4
RS
3113 (easy-menu-change '("Info") "Reference" (nreverse entries)))
3114 ;; Update last seen node.
6d2c8e3e 3115 (setq Info-menu-last-node (list Info-current-file Info-current-node)))
75a209d4
RS
3116 ;; Try to avoid entering infinite beep mode in case of errors.
3117 (error (ding))))
3118
a384cab3 3119\f
50163b00 3120(defun Info-copy-current-node-name (&optional arg)
126f5d4d 3121 "Put the name of the current info node into the kill ring.
50163b00
JL
3122The name of the info file is prepended to the node name in parentheses.
3123With a zero prefix arg, put the name inside a function call to `info'."
3124 (interactive "P")
126f5d4d
MB
3125 (unless Info-current-node
3126 (error "No current info node"))
50163b00
JL
3127 (let ((node (concat "(" (file-name-nondirectory
3128 (or (and (stringp Info-current-file)
3129 Info-current-file)
3130 buffer-file-name
3131 ""))
3132 ")" Info-current-node)))
3133 (if (zerop (prefix-numeric-value arg))
3134 (setq node (concat "(info \"" node "\")")))
3135 (kill-new node)
3136 (message "%s" node)))
126f5d4d
MB
3137
3138\f
a384cab3 3139;; Info mode is suitable only for specially formatted data.
68bc119c 3140(put 'Info-mode 'mode-class 'special)
2fa8f8a4 3141(put 'Info-mode 'no-clone-indirect t)
a384cab3
JB
3142
3143(defun Info-mode ()
fdf4b680 3144 "Info mode provides commands for browsing through the Info documentation tree.
a384cab3
JB
3145Documentation in Info is divided into \"nodes\", each of which discusses
3146one topic and contains references to other nodes which discuss related
3147topics. Info has commands to follow the references and show you other nodes.
3148
fdf4b680 3149\\<Info-mode-map>\
a384cab3 3150\\[Info-help] Invoke the Info tutorial.
6b136ab9 3151\\[Info-exit] Quit Info: reselect previously selected buffer.
a384cab3
JB
3152
3153Selecting other nodes:
21d5959f
RS
3154\\[Info-mouse-follow-nearest-node]
3155 Follow a node reference you click on.
3156 This works with menu items, cross references, and
3157 the \"next\", \"previous\" and \"up\", depending on where you click.
6b136ab9 3158\\[Info-follow-nearest-node] Follow a node reference near point, like \\[Info-mouse-follow-nearest-node].
a384cab3 3159\\[Info-next] Move to the \"next\" node of this node.
bc2ada62 3160\\[Info-prev] Move to the \"previous\" node of this node.
a384cab3
JB
3161\\[Info-up] Move \"up\" from this node.
3162\\[Info-menu] Pick menu item specified by name (or abbreviation).
e8042313 3163 Picking a menu item causes another node to be selected.
aea2a8da 3164\\[Info-directory] Go to the Info directory node.
a384cab3 3165\\[Info-follow-reference] Follow a cross reference. Reads name of reference.
424b9d3e
JL
3166\\[Info-history-back] Move back in history to the last node you were at.
3167\\[Info-history-forward] Move forward in history to the node you returned from after using \\[Info-history-back].
bc224645
JL
3168\\[Info-history] Go to menu of visited nodes.
3169\\[Info-toc] Go to table of contents of the current Info file.
6b136ab9
DL
3170\\[Info-top-node] Go to the Top node of this file.
3171\\[Info-final-node] Go to the final node in this file.
3172\\[Info-backward-node] Go backward one node, considering all nodes as forming one sequence.
3173\\[Info-forward-node] Go forward one node, considering all nodes as forming one sequence.
bc224645
JL
3174\\[Info-index] Look up a topic in this file's Index and move to that node.
3175\\[Info-index-next] (comma) Move to the next match from a previous \\<Info-mode-map>\\[Info-index] command.
3176\\[info-apropos] Look for a string in the indices of all manuals.
a384cab3
JB
3177
3178Moving within a node:
177c3549 3179\\[Info-scroll-up] Normally, scroll forward a full screen.
e8042313
PJ
3180 Once you scroll far enough in a node that its menu appears on the
3181 screen but after point, the next scroll moves into its first
3182 subnode. When after all menu items (or if there is no menu),
3183 move up to the parent node.
177c3549 3184\\[Info-scroll-down] Normally, scroll backward. If the beginning of the buffer is
e8042313
PJ
3185 already visible, try to go to the previous menu entry, or up
3186 if there is none.
399c88ad 3187\\[beginning-of-buffer] Go to beginning of node.
a384cab3 3188
a384cab3 3189Advanced commands:
c2a87d74 3190\\[Info-copy-current-node-name] Put name of current info node in the kill ring.
8a7757f6 3191\\[clone-buffer] Select a new cloned Info buffer in another window.
a384cab3 3192\\[Info-edit] Edit contents of selected node.
bc224645
JL
31931 .. 9 Pick first ... ninth item in node's menu.
3194 Every third `*' is highlighted to help pick the right number.
a384cab3 3195\\[Info-goto-node] Move to node specified by name.
e8042313 3196 You may include a filename as well, as (FILENAME)NODENAME.
552775bd 3197\\[universal-argument] \\[info] Move to new Info file with completion.
bc224645 3198\\[universal-argument] N \\[info] Select Info buffer with prefix number in the name *info*<N>.
a384cab3 3199\\[Info-search] Search through this Info file for specified regexp,
e8042313 3200 and select the node in which the next occurrence is found.
bc224645 3201\\[Info-search-case-sensitively] Search through this Info file for specified regexp case-sensitively.
8a7757f6 3202\\[Info-search-next] Search for another occurrence of regexp
89b064a9 3203 from a previous \\<Info-mode-map>\\[Info-search] command.
552775bd
RS
3204\\[Info-next-reference] Move cursor to next cross-reference or menu item.
3205\\[Info-prev-reference] Move cursor to previous cross-reference or menu item."
a384cab3
JB
3206 (kill-all-local-variables)
3207 (setq major-mode 'Info-mode)
3208 (setq mode-name "Info")
f73299f3 3209 (setq tab-width 8)
a384cab3 3210 (use-local-map Info-mode-map)
75a209d4 3211 (add-hook 'activate-menubar-hook 'Info-menu-update nil t)
a384cab3
JB
3212 (set-syntax-table text-mode-syntax-table)
3213 (setq local-abbrev-table text-mode-abbrev-table)
3214 (setq case-fold-search t)
3215 (setq buffer-read-only t)
a384cab3
JB
3216 (make-local-variable 'Info-current-file)
3217 (make-local-variable 'Info-current-subfile)
3218 (make-local-variable 'Info-current-node)
3219 (make-local-variable 'Info-tag-table-marker)
c5fe2ff1
RS
3220 (setq Info-tag-table-marker (make-marker))
3221 (make-local-variable 'Info-tag-table-buffer)
3222 (setq Info-tag-table-buffer nil)
a384cab3 3223 (make-local-variable 'Info-history)
424b9d3e 3224 (make-local-variable 'Info-history-forward)
1143a6b0 3225 (make-local-variable 'Info-index-alternatives)
dde7f979
SM
3226 (setq header-line-format
3227 (if Info-use-header-line
3536c121 3228 '(:eval (get-text-property (point-min) 'header-line))
dde7f979 3229 nil)) ; so the header line isn't displayed
7210b6f5 3230 (set (make-local-variable 'tool-bar-map) info-tool-bar-map)
93480d70
RS
3231 ;; This is for the sake of the invisible text we use handling titles.
3232 (make-local-variable 'line-move-ignore-invisible)
3233 (setq line-move-ignore-invisible t)
9d9d10ec
LH
3234 (make-local-variable 'desktop-save-buffer)
3235 (setq desktop-save-buffer 'Info-desktop-buffer-misc-data)
b7aeabeb 3236 (add-hook 'clone-buffer-hook 'Info-clone-buffer-hook nil t)
3d12b331 3237 (add-hook 'change-major-mode-hook 'font-lock-defontify nil t)
d457219d 3238 (add-hook 'isearch-mode-hook 'Info-isearch-start nil t)
0b02cda9
JL
3239 (set (make-local-variable 'isearch-search-fun-function)
3240 'Info-isearch-search)
3241 (set (make-local-variable 'isearch-wrap-function)
3242 'Info-isearch-wrap)
3243 (set (make-local-variable 'isearch-push-state-function)
3244 'Info-isearch-push-state)
3245 (set (make-local-variable 'search-whitespace-regexp)
3246 Info-search-whitespace-regexp)
a384cab3
JB
3247 (Info-set-mode-line)
3248 (run-hooks 'Info-mode-hook))
3249
4fceda3c
SM
3250(defun Info-clone-buffer-hook ()
3251 (when (bufferp Info-tag-table-buffer)
3252 (setq Info-tag-table-buffer
4ce5c223
SM
3253 (with-current-buffer Info-tag-table-buffer (clone-buffer))))
3254 (let ((m Info-tag-table-marker))
3255 (when (markerp m)
15533ae2 3256 (setq Info-tag-table-marker
4ce5c223 3257 (if (and (marker-position m) (bufferp Info-tag-table-buffer))
15533ae2
SM
3258 (with-current-buffer Info-tag-table-buffer
3259 (copy-marker (marker-position m)))
3260 (make-marker))))))
4fceda3c 3261
7210b6f5
DL
3262(defvar Info-edit-map (let ((map (make-sparse-keymap)))
3263 (set-keymap-parent map text-mode-map)
3264 (define-key map "\C-c\C-c" 'Info-cease-edit)
3265 map)
a384cab3 3266 "Local keymap used within `e' command of Info.")
a384cab3
JB
3267
3268;; Info-edit mode is suitable only for specially formatted data.
68bc119c 3269(put 'Info-edit-mode 'mode-class 'special)
a384cab3
JB
3270
3271(defun Info-edit-mode ()
3272 "Major mode for editing the contents of an Info node.
a426b157 3273Like text mode with the addition of `Info-cease-edit'
a384cab3
JB
3274which returns to Info mode for browsing.
3275\\{Info-edit-map}"
a384cab3
JB
3276 (use-local-map Info-edit-map)
3277 (setq major-mode 'Info-edit-mode)
3278 (setq mode-name "Info Edit")
3279 (kill-local-variable 'mode-line-buffer-identification)
3280 (setq buffer-read-only nil)
2c609f53 3281 (force-mode-line-update)
e82c28f9
KH
3282 (buffer-enable-undo (current-buffer))
3283 (run-hooks 'Info-edit-mode-hook))
3284
3285(defun Info-edit ()
3286 "Edit the contents of this Info node.
3287Allowed only if variable `Info-enable-edit' is non-nil."
3288 (interactive)
3289 (or Info-enable-edit
3290 (error "Editing info nodes is not enabled"))
3291 (Info-edit-mode)
8ab3e50b 3292 (message "%s" (substitute-command-keys
760d5cb3 3293 "Editing: Type \\<Info-edit-map>\\[Info-cease-edit] to return to info")))
a384cab3
JB
3294
3295(defun Info-cease-edit ()
3296 "Finish editing Info node; switch back to Info proper."
3297 (interactive)
3298 ;; Do this first, so nothing has changed if user C-g's at query.
3299 (and (buffer-modified-p)
3300 (y-or-n-p "Save the file? ")
3301 (save-buffer))
3302 (use-local-map Info-mode-map)
3303 (setq major-mode 'Info-mode)
3304 (setq mode-name "Info")
3305 (Info-set-mode-line)
3306 (setq buffer-read-only t)
2c609f53 3307 (force-mode-line-update)
a384cab3
JB
3308 (and (marker-position Info-tag-table-marker)
3309 (buffer-modified-p)
3310 (message "Tags may have changed. Use Info-tagify if necessary")))
f0a8a3f1 3311\f
f88ab1c9 3312(defvar Info-file-list-for-emacs
02c9f5cd 3313 '("ediff" "eudc" "forms" "gnus" "info" ("Info" . "info") ("mh" . "mh-e")
906b3163
EZ
3314 "sc" "message" ("dired" . "dired-x") "viper" "vip" "idlwave"
3315 ("c" . "ccmode") ("c++" . "ccmode") ("objc" . "ccmode")
3316 ("java" . "ccmode") ("idl" . "ccmode") ("pike" . "ccmode")
af718538
EZ
3317 ("skeleton" . "autotype") ("auto-insert" . "autotype")
3318 ("copyright" . "autotype") ("executable" . "autotype")
3319 ("time-stamp" . "autotype") ("quickurl" . "autotype")
3320 ("tempo" . "autotype") ("hippie-expand" . "autotype")
807378ad
EZ
3321 ("cvs" . "pcl-cvs") ("ada" . "ada-mode") "calc"
3322 ("calcAlg" . "calc") ("calcDigit" . "calc") ("calcVar" . "calc")
906b3163
EZ
3323 "ebrowse" "eshell" "cl" "reftex" "speedbar" "widget" "woman"
3324 ("mail-header" . "emacs-mime") ("mail-content" . "emacs-mime")
3325 ("mail-encode" . "emacs-mime") ("mail-decode" . "emacs-mime")
3326 ("rfc2045" . "emacs-mime")
3327 ("rfc2231" . "emacs-mime") ("rfc2047" . "emacs-mime")
3328 ("rfc2045" . "emacs-mime") ("rfc1843" . "emacs-mime")
3329 ("ietf-drums" . "emacs-mime") ("quoted-printable" . "emacs-mime")
3330 ("binhex" . "emacs-mime") ("uudecode" . "emacs-mime")
3331 ("mailcap" . "emacs-mime") ("mm" . "emacs-mime")
3332 ("mml" . "emacs-mime"))
f88ab1c9
RS
3333 "List of Info files that describe Emacs commands.
3334An element can be a file name, or a list of the form (PREFIX . FILE)
3335where PREFIX is a name prefix and FILE is the file to look in.
3336If the element is just a file name, the file name also serves as the prefix.")
3337
f0a8a3f1 3338(defun Info-find-emacs-command-nodes (command)
f88ab1c9 3339 "Return a list of locations documenting COMMAND.
f57b2cd8
RS
3340The `info-file' property of COMMAND says which Info manual to search.
3341If COMMAND has no property, the variable `Info-file-list-for-emacs'
3342defines heuristics for which Info manual to try.
fdf4b680 3343The locations are of the format used in `Info-history', i.e.
02c9f5cd
JL
3344\(FILENAME NODENAME BUFFERPOS\), where BUFFERPOS is the line number
3345in the first element of the returned list (which is treated specially in
3346`Info-goto-emacs-command-node'), and 0 for the rest elements of a list."
3347 (let ((where '()) line-number
b37daea4 3348 (cmd-desc (concat "^\\* +" (regexp-quote (symbol-name command))
f254e21f 3349 "\\( <[0-9]+>\\)?:\\s *\\(.*\\)\\."
02c9f5cd 3350 "\\(?:[ \t\n]+(line +\\([0-9]+\\))\\)?"))
f88ab1c9
RS
3351 (info-file "emacs")) ;default
3352 ;; Determine which info file this command is documented in.
3353 (if (get command 'info-file)
3354 (setq info-file (get command 'info-file))
3355 ;; If it doesn't say explicitly, test its name against
3356 ;; various prefixes that we know.
3357 (let ((file-list Info-file-list-for-emacs))
3358 (while file-list
3359 (let* ((elt (car file-list))
3360 (name (if (consp elt)
3361 (car elt)
3362 elt))
3363 (file (if (consp elt) (cdr elt) elt))
cc3f6c32 3364 (case-fold-search nil)
f57b2cd8 3365 (regexp (concat "\\`" (regexp-quote name)
f88ab1c9
RS
3366 "\\(\\'\\|-\\)")))
3367 (if (string-match regexp (symbol-name command))
3368 (setq info-file file file-list nil))
3369 (setq file-list (cdr file-list))))))
218c2cc7
EZ
3370 (Info-find-node info-file "Top")
3371 (or (and (search-forward "\n* menu:" nil t)
3372 (re-search-forward "\n\\* \\(.*\\<Index\\>\\)" nil t))
3373 (error "Info file `%s' appears to lack an index" info-file))
3374 (goto-char (match-beginning 1))
3375 ;; Bind Info-history to nil, to prevent the index nodes from
3376 ;; getting into the node history.
3377 (let ((Info-history nil)
141b49f5
JL
3378 (Info-history-list nil)
3379 node (nodes (Info-index-nodes)))
3380 (Info-goto-node (car nodes))
218c2cc7
EZ
3381 (while
3382 (progn
3383 (goto-char (point-min))
3384 (while (re-search-forward cmd-desc nil t)
3385 (setq where
3386 (cons (list Info-current-file
3387 (match-string-no-properties 2)
760d5cb3 3388 0)
02c9f5cd
JL
3389 where))
3390 (setq line-number (and (match-beginning 3)
3391 (string-to-number (match-string 3)))))
141b49f5 3392 (and (setq nodes (cdr nodes) node (car nodes))))
218c2cc7 3393 (Info-goto-node node)))
02c9f5cd
JL
3394 (if (and line-number where)
3395 (cons (list (nth 0 (car where)) (nth 1 (car where)) line-number)
3396 (cdr where))
3397 where)))
f0a8a3f1 3398
02c9f5cd 3399;;;###autoload (put 'Info-goto-emacs-command-node 'info-file "emacs")
f0a8a3f1
RM
3400;;;###autoload
3401(defun Info-goto-emacs-command-node (command)
e0568e86 3402 "Go to the Info node in the Emacs manual for command COMMAND.
30db89f9 3403The command is found by looking up in Emacs manual's indices
f57b2cd8 3404or in another manual found via COMMAND's `info-file' property or
1ed6e0c3
JPW
3405the variable `Info-file-list-for-emacs'.
3406COMMAND must be a symbol or string."
f0a8a3f1 3407 (interactive "CFind documentation for command: ")
fafb00dc
MY
3408 ;; If command is given as a string, convert it to a symbol.
3409 (if (stringp command)
1ed6e0c3 3410 (setq command (intern command)))
f0a8a3f1
RM
3411 (or (commandp command)
3412 (signal 'wrong-type-argument (list 'commandp command)))
3413 (let ((where (Info-find-emacs-command-nodes command)))
3414 (if where
3415 (let ((num-matches (length where)))
3416 ;; Get Info running, and pop to it in another window.
3417 (save-window-excursion
3418 (info))
89b064a9 3419 (or (eq major-mode 'Info-mode) (pop-to-buffer "*info*"))
218c2cc7
EZ
3420 ;; Bind Info-history to nil, to prevent the last Index node
3421 ;; visited by Info-find-emacs-command-nodes from being
3422 ;; pushed onto the history.
02c9f5cd
JL
3423 (let ((Info-history nil) (Info-history-list nil)
3424 (line-number (nth 2 (car where))))
3425 (Info-find-node (nth 0 (car where)) (nth 1 (car where)))
3426 (if (and (integerp line-number) (> line-number 0))
3427 (forward-line (1- line-number))))
f0a8a3f1
RM
3428 (if (> num-matches 1)
3429 (progn
218c2cc7
EZ
3430 ;; (car where) will be pushed onto Info-history
3431 ;; when/if they go to another node. Put the other
3432 ;; nodes that were found on the history.
f0a8a3f1 3433 (setq Info-history (nconc (cdr where) Info-history))
8ab3e50b 3434 (message "Found %d other entr%s. Use %s to see %s."
cedb118c
RS
3435 (1- num-matches)
3436 (if (> num-matches 2) "ies" "y")
424b9d3e 3437 (substitute-command-keys "\\[Info-history-back]")
cedb118c 3438 (if (> num-matches 2) "them" "it")))))
e9b81433 3439 (error "Couldn't find documentation for %s" command))))
f0a8a3f1 3440
02c9f5cd 3441;;;###autoload (put 'Info-goto-emacs-key-command-node 'info-file "emacs")
f0a8a3f1
RM
3442;;;###autoload
3443(defun Info-goto-emacs-key-command-node (key)
30db89f9
EZ
3444 "Go to the node in the Emacs manual which describes the command bound to KEY.
3445KEY is a string.
fdf4b680 3446Interactively, if the binding is `execute-extended-command', a command is read.
30db89f9 3447The command is found by looking up in Emacs manual's indices
f57b2cd8
RS
3448or in another manual found via COMMAND's `info-file' property or
3449the variable `Info-file-list-for-emacs'."
ea33749b 3450 (interactive "kFind documentation for key: ")
f0a8a3f1
RM
3451 (let ((command (key-binding key)))
3452 (cond ((null command)
9e5c2f50 3453 (message "%s is undefined" (key-description key)))
f0a8a3f1
RM
3454 ((and (interactive-p)
3455 (eq command 'execute-extended-command))
3456 (Info-goto-emacs-command-node
3457 (read-command "Find documentation for command: ")))
3458 (t
3459 (Info-goto-emacs-command-node command)))))
552775bd 3460\f
bbed5c3f 3461(defface Info-title-1-face
508bce68
SM
3462 '((((type tty pc) (class color)) :foreground "yellow" :weight bold)
3463 (t :height 1.2 :inherit Info-title-2-face))
bbed5c3f
GM
3464 "Face for Info titles at level 1."
3465 :group 'info)
3466
3467(defface Info-title-2-face
508bce68
SM
3468 '((((type tty pc) (class color)) :foreground "lightblue" :weight bold)
3469 (t :height 1.2 :inherit Info-title-3-face))
bbed5c3f
GM
3470 "Face for Info titles at level 2."
3471 :group 'info)
3472
3473(defface Info-title-3-face
508bce68
SM
3474 '((((type tty pc) (class color)) :weight bold)
3475 (t :height 1.2 :inherit Info-title-4-face))
bbed5c3f
GM
3476 "Face for Info titles at level 3."
3477 :group 'info)
3478
a8b883c2 3479(defface Info-title-4-face
508bce68
SM
3480 '((((type tty pc) (class color)) :weight bold)
3481 (t :weight bold :inherit variable-pitch))
a8b883c2
MB
3482 "Face for Info titles at level 4."
3483 :group 'info)
3484
973a3104
MB
3485(defface info-menu-header
3486 '((((type tty pc))
42121c23
SZ
3487 :underline t
3488 :weight bold)
973a3104 3489 (t
42121c23
SZ
3490 :inherit variable-pitch
3491 :weight bold))
973a3104
MB
3492 "Face for headers in Info menus."
3493 :group 'info)
3494
3536c121
LK
3495(defun Info-escape-percent (string)
3496 "Double all occurrences of `%' in STRING.
3497
3498Return a new string with all `%' characters replaced by `%%'.
3499Preserve text properties."
3500 (let ((start 0)
3501 (end (length string))
3502 mb me m matches)
3503 (save-match-data
3504 (while (and (< start end) (string-match "%" string start))
3505 (setq mb (match-beginning 0)
3506 me (1+ mb)
3507 m (substring string mb me)
1ed6e0c3
JPW
3508 matches (cons m
3509 (cons m
3510 (cons (substring string start mb)
3536c121
LK
3511 matches)))
3512 start me))
3513 (push (substring string start end) matches)
3514 (apply #'concat (nreverse matches)))))
3515
efb21aab
RS
3516(defvar Info-next-link-keymap
3517 (let ((keymap (make-sparse-keymap)))
3518 (define-key keymap [header-line mouse-1] 'Info-next)
3519 (define-key keymap [header-line mouse-2] 'Info-next)
3520 (define-key keymap [header-line down-mouse-1] 'ignore)
3521 (define-key keymap [mouse-2] 'Info-next)
3c1498ac 3522 (define-key keymap [follow-link] 'mouse-face)
efb21aab
RS
3523 keymap)
3524 "Keymap to put on the Next link in the text or the header line.")
3525
3526(defvar Info-prev-link-keymap
3527 (let ((keymap (make-sparse-keymap)))
3528 (define-key keymap [header-line mouse-1] 'Info-prev)
3529 (define-key keymap [header-line mouse-2] 'Info-prev)
3530 (define-key keymap [header-line down-mouse-1] 'ignore)
3531 (define-key keymap [mouse-2] 'Info-prev)
3c1498ac 3532 (define-key keymap [follow-link] 'mouse-face)
efb21aab
RS
3533 keymap)
3534 "Keymap to put on the Prev link in the text or the header line.")
3535
3536
3537(defvar Info-up-link-keymap
3538 (let ((keymap (make-sparse-keymap)))
3539 (define-key keymap [header-line mouse-1] 'Info-up)
3540 (define-key keymap [header-line mouse-2] 'Info-up)
3541 (define-key keymap [header-line down-mouse-1] 'ignore)
3542 (define-key keymap [mouse-2] 'Info-up)
3c1498ac 3543 (define-key keymap [follow-link] 'mouse-face)
efb21aab
RS
3544 keymap)
3545 "Keymap to put on the Up link in the text or the header line.")
3546
552775bd 3547(defun Info-fontify-node ()
8a7757f6
JL
3548 "Fontify the node."
3549 (save-excursion
3550 (let* ((inhibit-read-only t)
3551 (case-fold-search t)
3552 paragraph-markers
3553 (not-fontified-p ; the node hasn't already been fontified
3554 (not (let ((where (next-property-change (point-min))))
3555 (and where (not (= where (point-max)))))))
3556 (fontify-visited-p ; visited nodes need to be re-fontified
3557 (and Info-fontify-visited-nodes
3558 ;; Don't take time to refontify visited nodes in huge nodes
e14105f6
TTN
3559 (< (- (point-max) (point-min)) Info-fontify-maximum-menu-size)))
3560 rbeg rend)
8a7757f6
JL
3561
3562 ;; Fontify header line
3563 (goto-char (point-min))
3564 (when (and not-fontified-p (looking-at "^\\(File: [^,: \t]+,?[ \t]+\\)?"))
3565 (goto-char (match-end 0))
3566 (while (looking-at "[ \t]*\\([^:, \t\n]+\\):[ \t]+\\([^:,\t\n]+\\),?")
3567 (goto-char (match-end 0))
3568 (let* ((nbeg (match-beginning 2))
3569 (nend (match-end 2))
3570 (tbeg (match-beginning 1))
3571 (tag (match-string 1)))
3572 (if (string-equal tag "Node")
3573 (put-text-property nbeg nend 'font-lock-face 'info-header-node)
3574 (put-text-property nbeg nend 'font-lock-face 'info-header-xref)
3575 (put-text-property tbeg nend 'mouse-face 'highlight)
3576 (put-text-property tbeg nend
3577 'help-echo
3c1498ac 3578 (concat "mouse-2: Go to node "
8a7757f6
JL
3579 (buffer-substring nbeg nend)))
3580 ;; Always set up the text property keymap.
3581 ;; It will either be used in the buffer
3582 ;; or copied in the header line.
3583 (put-text-property tbeg nend 'keymap
3584 (cond
3585 ((equal tag "Prev") Info-prev-link-keymap)
3586 ((equal tag "Next") Info-next-link-keymap)
3587 ((equal tag "Up") Info-up-link-keymap))))))
3588 (when Info-use-header-line
3589 (goto-char (point-min))
3590 (let ((header-end (line-end-position))
3591 header)
3592 ;; If we find neither Next: nor Prev: link, show the entire
3593 ;; node header. Otherwise, don't show the File: and Node:
3594 ;; parts, to avoid wasting precious space on information that
3595 ;; is available in the mode line.
3596 (if (re-search-forward
3597 "\\(next\\|up\\|prev[ious]*\\): "
3598 header-end t)
3599 (progn
3600 (goto-char (match-beginning 1))
3601 (setq header (buffer-substring (point) header-end)))
3602 (if (re-search-forward "node:[ \t]*[^ \t]+[ \t]*" header-end t)
3603 (setq header
3604 (concat "No next, prev or up links -- "
3605 (buffer-substring (point) header-end)))
3606 (setq header (buffer-substring (point) header-end))))
3607 (put-text-property (point-min) (1+ (point-min))
3608 'header-line (Info-escape-percent header))
3609 ;; Hide the part of the first line
3610 ;; that is in the header, if it is just part.
3611 (unless (bobp)
3612 ;; Hide the punctuation at the end, too.
3613 (skip-chars-backward " \t,")
3614 (put-text-property (point) header-end 'invisible t)))))
3615
3616 ;; Fontify titles
3617 (goto-char (point-min))
3618 (when not-fontified-p
3619 (while (re-search-forward "\n\\([^ \t\n].+\\)\n\\(\\*+\\|=+\\|-+\\|\\.+\\)$"
3620 nil t)
3621 (let* ((c (preceding-char))
3622 (face
3623 (cond ((= c ?*) 'Info-title-1-face)
3624 ((= c ?=) 'Info-title-2-face)
3625 ((= c ?-) 'Info-title-3-face)
3626 (t 'Info-title-4-face))))
3627 (put-text-property (match-beginning 1) (match-end 1)
3628 'font-lock-face face))
3629 ;; This is a serious problem for trying to handle multiple
3630 ;; frame types at once. We want this text to be invisible
3631 ;; on frames that can display the font above.
3632 (when (memq (framep (selected-frame)) '(x pc w32 mac))
3633 (add-text-properties (1- (match-beginning 2)) (match-end 2)
3634 '(invisible t front-sticky nil rear-nonsticky t)))))
3635
3636 ;; Fontify cross references
3637 (goto-char (point-min))
3638 (when (or not-fontified-p fontify-visited-p)
3639 (while (re-search-forward "\\(\\*Note[ \n\t]+\\)\\([^:]*\\)\\(:[ \t]*\\([^.,:(]*\\)\\(\\(([^)]*)\\)[^.,:]*\\)?[,:]?\n?\\)" nil t)
3640 (let ((start (match-beginning 0))
3641 (next (point))
3642 other-tag)
3643 (when not-fontified-p
3644 (when Info-hide-note-references
fc11ddc8
JL
3645 (when (not (eq Info-hide-note-references 'hide))
3646 ;; *Note is often used where *note should have been
3647 (goto-char start)
3648 (skip-syntax-backward " ")
3649 (setq other-tag
3650 (cond ((memq (char-before) '(nil ?\. ?! ??))
3651 "See ")
3652 ((memq (char-before) '(?\, ?\; ?\: ?-))
3653 "see ")
3654 ((memq (char-before) '(?\( ?\[ ?\{))
3655 ;; Check whether the paren is preceded by
3656 ;; an end of sentence
3657 (skip-syntax-backward " (")
3658 (if (memq (char-before) '(nil ?\. ?! ??))
3659 "See "
3660 "see "))
3661 ((save-match-data (looking-at "\n\n"))
3662 "See "))))
8a7757f6
JL
3663 (goto-char next)
3664 (add-text-properties
3665 (match-beginning 1)
3666 (or (save-match-data
3667 ;; Don't hide \n after *Note
3668 (let ((start1 (match-beginning 1)))
3669 (if (string-match "\n" (match-string 1))
3670 (+ start1 (match-beginning 0)))))
3671 (match-end 1))
fc11ddc8 3672 (if other-tag
8a7757f6
JL
3673 `(display ,other-tag front-sticky nil rear-nonsticky t)
3674 '(invisible t front-sticky nil rear-nonsticky t))))
3675 (add-text-properties
3676 (match-beginning 2) (match-end 2)
3677 (list
3678 'help-echo (if (or (match-end 5)
3679 (not (equal (match-string 4) "")))
3680 (concat "mouse-2: go to " (or (match-string 5)
3681 (match-string 4)))
3682 "mouse-2: go to this node")
3683 'mouse-face 'highlight)))
3684 (when (or not-fontified-p fontify-visited-p)
e14105f6
TTN
3685 (setq rbeg (match-beginning 2)
3686 rend (match-end 2))
3687 (put-text-property
3688 rbeg rend
3689 'font-lock-face
3690 ;; Display visited nodes in a different face
3691 (if (and Info-fontify-visited-nodes
3692 (save-match-data
3693 (let* ((node (replace-regexp-in-string
3694 "^[ \t]+" ""
3695 (replace-regexp-in-string
3696 "[ \t\n]+" " "
3697 (or (match-string 5)
3698 (and (not (equal (match-string 4) ""))
3699 (match-string 4))
3700 (match-string 2)))))
3701 (file (file-name-nondirectory
3702 Info-current-file))
3703 (hl Info-history-list)
3704 res)
3705 (if (string-match "(\\([^)]+\\))\\([^)]*\\)" node)
3706 (setq file (file-name-nondirectory
3707 (match-string 1 node))
3708 node (if (equal (match-string 2 node) "")
3709 "Top"
3710 (match-string 2 node))))
3711 (while hl
3712 (if (and (string-equal node (nth 1 (car hl)))
3713 (string-equal file
3714 (file-name-nondirectory
3715 (nth 0 (car hl)))))
3716 (setq res (car hl) hl nil)
3717 (setq hl (cdr hl))))
3718 res))) 'info-xref-visited 'info-xref))
3719 ;; For multiline ref, unfontify newline and surrounding whitespace
3720 (save-excursion
3721 (goto-char rbeg)
3722 (save-match-data
3723 (while (re-search-forward "\\s-*\n\\s-*" rend t nil)
3724 (remove-text-properties (match-beginning 0)
3725 (match-end 0)
3726 '(font-lock-face t))))))
8a7757f6
JL
3727 (when not-fontified-p
3728 (when (memq Info-hide-note-references '(t hide))
3729 (add-text-properties (match-beginning 3) (match-end 3)
3730 '(invisible t front-sticky nil rear-nonsticky t))
3731 ;; Unhide the file name of the external reference in parens
89b064a9 3732 (if (and (match-string 6) (not (eq Info-hide-note-references 'hide)))
8a7757f6
JL
3733 (remove-text-properties (match-beginning 6) (match-end 6)
3734 '(invisible t front-sticky nil rear-nonsticky t)))
3735 ;; Unhide newline because hidden newlines cause too long lines
3736 (save-match-data
89b064a9
JL
3737 (let ((beg3 (match-beginning 3))
3738 (end3 (match-end 3)))
3739 (if (and (string-match "\n[ \t]*" (match-string 3))
3740 (not (save-match-data
3741 (save-excursion
3742 (goto-char (1+ end3))
3743 (looking-at "[.)]*$")))))
3744 (remove-text-properties (+ beg3 (match-beginning 0))
3745 (+ beg3 (match-end 0))
8a7757f6
JL
3746 '(invisible t front-sticky nil rear-nonsticky t))))))
3747 (when (and Info-refill-paragraphs Info-hide-note-references)
3748 (push (set-marker (make-marker) start)
3749 paragraph-markers))))))
3750
3751 ;; Refill paragraphs (experimental feature)
3752 (when (and not-fontified-p
3753 Info-refill-paragraphs
3754 paragraph-markers)
3755 (let ((fill-nobreak-invisible t)
3756 (fill-individual-varying-indent nil)
3757 (paragraph-start "\f\\|[ \t]*[-*]\\|[ \t]*$")
3758 (paragraph-separate ".*\\.[ \t]*\n[ \t]\\|[ \t]*[-*]\\|[ \t\f]*$")
3759 (adaptive-fill-mode nil))
3760 (goto-char (point-max))
3761 (while paragraph-markers
3762 (let ((m (car paragraph-markers)))
3763 (setq paragraph-markers (cdr paragraph-markers))
3764 (when (< m (point))
3765 (goto-char m)
3766 (beginning-of-line)
3767 (let ((beg (point)))
3768 (when (zerop (forward-paragraph))
3769 (fill-individual-paragraphs beg (point) nil nil)
3770 (goto-char beg))))
3771 (set-marker m nil)))))
3772
3773 ;; Fontify menu items
3774 (goto-char (point-min))
3775 (when (and (or not-fontified-p fontify-visited-p)
3776 (search-forward "\n* Menu:" nil t)
141b49f5 3777 (not (Info-index-node))
8a7757f6
JL
3778 ;; Don't take time to annotate huge menus
3779 (< (- (point-max) (point)) Info-fontify-maximum-menu-size))
3780 (let ((n 0)
3781 cont)
3782 (while (re-search-forward
3783 (concat "^\\* +\\(" Info-menu-entry-name-re "\\)\\(:"
3784 Info-node-spec-re "\\([ \t]*\\)\\)")
3785 nil t)
3786 (when not-fontified-p
3787 (setq n (1+ n))
3788 (if (and (<= n 9) (zerop (% n 3))) ; visual aids to help with 1-9 keys
3789 (put-text-property (match-beginning 0)
3790 (1+ (match-beginning 0))
3791 'font-lock-face 'info-menu-5)))
3792 (when not-fontified-p
3793 (add-text-properties
3794 (match-beginning 1) (match-end 1)
3795 (list
3796 'help-echo (if (match-end 3)
3797 (concat "mouse-2: go to " (match-string 3))
3798 "mouse-2: go to this node")
3799 'mouse-face 'highlight)))
3800 (when (or not-fontified-p fontify-visited-p)
3801 (add-text-properties
3802 (match-beginning 1) (match-end 1)
3803 (list
3804 'font-lock-face
3805 ;; Display visited menu items in a different face
3806 (if (and Info-fontify-visited-nodes
3807 (save-match-data
3808 (let ((node (if (equal (match-string 3) "")
3809 (match-string 1)
3810 (match-string 3)))
3811 (file (file-name-nondirectory Info-current-file))
3812 (hl Info-history-list)
3813 res)
3814 (if (string-match "(\\([^)]+\\))\\([^)]*\\)" node)
836bb478
JL
3815 (setq file (file-name-nondirectory
3816 (match-string 1 node))
8a7757f6
JL
3817 node (if (equal (match-string 2 node) "")
3818 "Top"
3819 (match-string 2 node))))
3820 (while hl
3821 (if (and (string-equal node (nth 1 (car hl)))
3822 (string-equal file
3823 (file-name-nondirectory
3824 (nth 0 (car hl)))))
3825 (setq res (car hl) hl nil)
3826 (setq hl (cdr hl))))
3827 res))) 'info-xref-visited 'info-xref))))
3828 (when (and not-fontified-p (memq Info-hide-note-references '(t hide)))
3829 (put-text-property (match-beginning 2) (1- (match-end 6))
3830 'invisible t)
3831 ;; Unhide the file name in parens
3832 (if (and (match-end 4) (not (eq (char-after (match-end 4)) ?.)))
3833 (remove-text-properties (match-beginning 4) (match-end 4)
3834 '(invisible t)))
3835 ;; We need a stretchable space like :align-to but with
3836 ;; a minimum value.
3837 (put-text-property (1- (match-end 6)) (match-end 6) 'display
3838 (if (>= 22 (- (match-end 1)
3839 (match-beginning 0)))
3840 '(space :align-to 24)
3841 '(space :width 2)))
3842 (setq cont (looking-at "."))
3843 (while (and (= (forward-line 1) 0)
3844 (looking-at "\\([ \t]+\\)[^*\n]"))
3845 (put-text-property (match-beginning 1) (1- (match-end 1))
3846 'invisible t)
3847 (put-text-property (1- (match-end 1)) (match-end 1)
3848 'display
3849 (if cont
3850 '(space :align-to 26)
3851 '(space :align-to 24)))
3852 (setq cont t))))))
3853
3854 ;; Fontify menu headers
3855 ;; Add the face `info-menu-header' to any header before a menu entry
3856 (goto-char (point-min))
3857 (when (and not-fontified-p (re-search-forward "^\\* Menu:" nil t))
3858 (put-text-property (match-beginning 0) (match-end 0)
3859 'font-lock-face 'info-menu-header)
3860 (while (re-search-forward "\n\n\\([^*\n ].*\\)\n\n?[*]" nil t)
3861 (put-text-property (match-beginning 1) (match-end 1)
3862 'font-lock-face 'info-menu-header)))
3863
836bb478
JL
3864 ;; Hide index line numbers
3865 (goto-char (point-min))
141b49f5 3866 (when (and not-fontified-p (Info-index-node))
836bb478
JL
3867 (while (re-search-forward "[ \t\n]*(line +[0-9]+)" nil t)
3868 (put-text-property (match-beginning 0) (match-end 0)
3869 'invisible t)))
3870
8a7757f6
JL
3871 ;; Fontify http and ftp references
3872 (goto-char (point-min))
3873 (when not-fontified-p
3874 (while (re-search-forward "[hf]t?tp://[^ \t\n\"`({<>})']+" nil t)
3875 (add-text-properties (match-beginning 0) (match-end 0)
3876 '(font-lock-face info-xref
3877 mouse-face highlight
3878 help-echo "mouse-2: go to this URL"))))
3879
3880 (set-buffer-modified-p nil))))
c5fe2ff1
RS
3881\f
3882
3883;; When an Info buffer is killed, make sure the associated tags buffer
3884;; is killed too.
3885(defun Info-kill-buffer ()
3886 (and (eq major-mode 'Info-mode)
3887 Info-tag-table-buffer
3888 (kill-buffer Info-tag-table-buffer)))
3889
3890(add-hook 'kill-buffer-hook 'Info-kill-buffer)
3cb6768f
EL
3891
3892;;; Speedbar support:
3893;; These functions permit speedbar to display the "tags" in the
3894;; current info node.
96ee3f29 3895(eval-when-compile (require 'speedbar))
3cb6768f 3896
96ee3f29
KH
3897(defvar Info-speedbar-key-map nil
3898 "Keymap used when in the info display mode.")
3cb6768f 3899
96ee3f29
KH
3900(defun Info-install-speedbar-variables ()
3901 "Install those variables used by speedbar to enhance Info."
3902 (if Info-speedbar-key-map
3903 nil
3904 (setq Info-speedbar-key-map (speedbar-make-specialized-keymap))
3905
3906 ;; Basic tree features
3907 (define-key Info-speedbar-key-map "e" 'speedbar-edit-line)
3908 (define-key Info-speedbar-key-map "\C-m" 'speedbar-edit-line)
3909 (define-key Info-speedbar-key-map "+" 'speedbar-expand-line)
3910 (define-key Info-speedbar-key-map "-" 'speedbar-contract-line)
3911 )
3912
3913 (speedbar-add-expansion-list '("Info" Info-speedbar-menu-items
3914 Info-speedbar-key-map
3915 Info-speedbar-hierarchy-buttons)))
3cb6768f
EL
3916
3917(defvar Info-speedbar-menu-items
96ee3f29
KH
3918 '(["Browse Node" speedbar-edit-line t]
3919 ["Expand Node" speedbar-expand-line
3920 (save-excursion (beginning-of-line)
3921 (looking-at "[0-9]+: *.\\+. "))]
3922 ["Contract Node" speedbar-contract-line
3923 (save-excursion (beginning-of-line)
3924 (looking-at "[0-9]+: *.-. "))]
3925 )
3cb6768f
EL
3926 "Additional menu-items to add to speedbar frame.")
3927
96ee3f29
KH
3928;; Make sure our special speedbar major mode is loaded
3929(if (featurep 'speedbar)
3930 (Info-install-speedbar-variables)
3931 (add-hook 'speedbar-load-hook 'Info-install-speedbar-variables))
3932
3933;;; Info hierarchy display method
3934;;;###autoload
3935(defun Info-speedbar-browser ()
3936 "Initialize speedbar to display an info node browser.
3937This will add a speedbar major display mode."
3938 (interactive)
3939 (require 'speedbar)
3940 ;; Make sure that speedbar is active
3941 (speedbar-frame-mode 1)
3942 ;; Now, throw us into Info mode on speedbar.
3943 (speedbar-change-initial-expansion-list "Info")
3944 )
3945
7210b6f5
DL
3946(eval-when-compile (defvar speedbar-attached-frame))
3947
96ee3f29
KH
3948(defun Info-speedbar-hierarchy-buttons (directory depth &optional node)
3949 "Display an Info directory hierarchy in speedbar.
3950DIRECTORY is the current directory in the attached frame.
3951DEPTH is the current indentation depth.
3952NODE is an optional argument that is used to represent the
3953specific node to expand."
3954 (if (and (not node)
3955 (save-excursion (goto-char (point-min))
1bcedb3b
RS
3956 (let ((case-fold-search t))
3957 (looking-at "Info Nodes:"))))
96ee3f29
KH
3958 ;; Update our "current node" maybe?
3959 nil
3960 ;; We cannot use the generic list code, that depends on all leaves
3961 ;; being known at creation time.
3962 (if (not node)
3963 (speedbar-with-writable (insert "Info Nodes:\n")))
5fdc7997
EL
3964 (let ((completions nil)
3965 (cf (selected-frame)))
3966 (select-frame speedbar-attached-frame)
3967 (save-window-excursion
3968 (setq completions
3969 (Info-speedbar-fetch-file-nodes (or node '"(dir)top"))))
3970 (select-frame cf)
96ee3f29
KH
3971 (if completions
3972 (speedbar-with-writable
dde7f979 3973 (dolist (completion completions)
96ee3f29 3974 (speedbar-make-tag-line 'bracket ?+ 'Info-speedbar-expand-node
dde7f979
SM
3975 (cdr completion)
3976 (car completion)
96ee3f29 3977 'Info-speedbar-goto-node
dde7f979
SM
3978 (cdr completion)
3979 'info-xref depth))
96ee3f29
KH
3980 t)
3981 nil))))
399c88ad 3982
96ee3f29 3983(defun Info-speedbar-goto-node (text node indent)
d1268e52 3984 "When user clicks on TEXT, go to an info NODE.
96ee3f29 3985The INDENT level is ignored."
760d5cb3
GM
3986 (select-frame speedbar-attached-frame)
3987 (let* ((buff (or (get-buffer "*info*")
3988 (progn (info) (get-buffer "*info*"))))
3989 (bwin (get-buffer-window buff 0)))
3990 (if bwin
3991 (progn
3992 (select-window bwin)
3993 (raise-frame (window-frame bwin)))
3994 (if speedbar-power-click
3995 (let ((pop-up-frames t)) (select-window (display-buffer buff)))
3996 (select-frame speedbar-attached-frame)
3997 (switch-to-buffer buff)))
dde7f979
SM
3998 (if (not (string-match "^(\\([^)]+\\))\\([^.]+\\)$" node))
3999 (error "Invalid node %s" node)
4000 (Info-find-node (match-string 1 node) (match-string 2 node))
760d5cb3
GM
4001 ;; If we do a find-node, and we were in info mode, restore
4002 ;; the old default method. Once we are in info mode, it makes
4003 ;; sense to return to whatever method the user was using before.
4004 (if (string= speedbar-initial-expansion-list-name "Info")
4005 (speedbar-change-initial-expansion-list
4006 speedbar-previously-used-expansion-list-name)))))
96ee3f29
KH
4007
4008(defun Info-speedbar-expand-node (text token indent)
4009 "Expand the node the user clicked on.
4010TEXT is the text of the button we clicked on, a + or - item.
4011TOKEN is data related to this node (NAME . FILE).
4012INDENT is the current indentation depth."
4013 (cond ((string-match "+" text) ;we have to expand this file
4014 (speedbar-change-expand-button-char ?-)
4015 (if (speedbar-with-writable
760d5cb3
GM
4016 (save-excursion
4017 (end-of-line) (forward-char 1)
4018 (Info-speedbar-hierarchy-buttons nil (1+ indent) token)))
96ee3f29
KH
4019 (speedbar-change-expand-button-char ?-)
4020 (speedbar-change-expand-button-char ??)))
4021 ((string-match "-" text) ;we have to contract this node
4022 (speedbar-change-expand-button-char ?+)
4023 (speedbar-delete-subblock indent))
4024 (t (error "Ooops... not sure what to do")))
4025 (speedbar-center-buffer-smartly))
4026
4027(defun Info-speedbar-fetch-file-nodes (nodespec)
4028 "Fetch the subnodes from the info NODESPEC.
4029NODESPEC is a string of the form: (file)node.
4030Optional THISFILE represends the filename of"
4031 (save-excursion
4032 ;; Set up a buffer we can use to fake-out Info.
4033 (set-buffer (get-buffer-create "*info-browse-tmp*"))
4034 (if (not (equal major-mode 'Info-mode))
4035 (Info-mode))
4036 ;; Get the node into this buffer
dde7f979
SM
4037 (if (not (string-match "^(\\([^)]+\\))\\([^.]+\\)$" nodespec))
4038 (error "Invalid node specification %s" nodespec)
4039 (Info-find-node (match-string 1 nodespec) (match-string 2 nodespec)))
96ee3f29
KH
4040 ;; Scan the created buffer
4041 (goto-char (point-min))
4042 (let ((completions nil)
1bcedb3b 4043 (case-fold-search t)
96ee3f29
KH
4044 (thisfile (progn (string-match "^(\\([^)]+\\))" nodespec)
4045 (match-string 1 nodespec))))
4046 ;; Always skip the first one...
4047 (re-search-forward "\n\\* \\([^:\t\n]*\\):" nil t)
4048 (while (re-search-forward "\n\\* \\([^:\t\n]*\\):" nil t)
4049 (let ((name (match-string 1)))
dde7f979
SM
4050 (push (cons name
4051 (if (looking-at " *\\(([^)]+)[^.\n]+\\)\\.")
4052 (match-string 1)
4053 (if (looking-at " *\\(([^)]+)\\)\\.")
4054 (concat (match-string 1) "Top")
4055 (concat "(" thisfile ")"
4056 (if (looking-at " \\([^.]+\\).")
4057 (match-string 1)
4058 name)))))
4059 completions)))
96ee3f29
KH
4060 (nreverse completions))))
4061
4062;;; Info mode node listing
a1637357 4063;; FIXME: Seems not to be used. -stef
3cb6768f
EL
4064(defun Info-speedbar-buttons (buffer)
4065 "Create a speedbar display to help navigation in an Info file.
4066BUFFER is the buffer speedbar is requesting buttons for."
96ee3f29 4067 (if (save-excursion (goto-char (point-min))
1bcedb3b
RS
4068 (let ((case-fold-search t))
4069 (not (looking-at "Info Nodes:"))))
96ee3f29
KH
4070 (erase-buffer))
4071 (Info-speedbar-hierarchy-buttons nil 0)
4072 )
49116ac0 4073
48e5b471
DL
4074(dolist (mess '("^Node has no Previous$"
4075 "^No menu in this node$"
4076 "^Node has no Next$"
399c88ad
SS
4077 "^No cross-references in this node^"
4078 search-failed
48e5b471
DL
4079 "^No \".*\" in index$"))
4080 (add-to-list 'debug-ignored-errors mess))
4081
f3a45db0
LH
4082;;;; Desktop support
4083
4084(defun Info-desktop-buffer-misc-data (desktop-dirname)
4085 "Auxiliary information to be saved in desktop file."
c9ce1e40
JL
4086 (if (not (member Info-current-file '("apropos" "history" "toc")))
4087 (list Info-current-file Info-current-node)))
f3a45db0
LH
4088
4089;;;###autoload
4090(defun Info-restore-desktop-buffer (desktop-buffer-file-name
4091 desktop-buffer-name
4092 desktop-buffer-misc)
4093 "Restore an info buffer specified in a desktop file."
4094 (let ((first (nth 0 desktop-buffer-misc))
4095 (second (nth 1 desktop-buffer-misc)))
4096 (when (and first second)
c9ce1e40
JL
4097 (when desktop-buffer-name
4098 (set-buffer (get-buffer-create desktop-buffer-name))
4099 (Info-mode))
2a3f604d 4100 (Info-find-node first second)
f3a45db0
LH
4101 (current-buffer))))
4102
49116ac0
JB
4103(provide 'info)
4104
ab5796a9 4105;;; arch-tag: f2480fe2-2139-40c1-a49b-6314991164ac
1a06eabd 4106;;; info.el ends here