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