diff options
author | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1995-12-22 16:47:20 +0000 |
---|---|---|
committer | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1995-12-22 16:47:20 +0000 |
commit | ec18f98a8dd00dbc9bdc71858b395712564182bf (patch) | |
tree | f31db27a0d475ab3bb7d62153ec5f12c4d691b2e /gnu/usr.bin/texinfo/emacs | |
parent | 0ea8ce66e7dba7c3b169b0e15553a973df9e3570 (diff) |
Import of texinfo-3.6 from FSF
Diffstat (limited to 'gnu/usr.bin/texinfo/emacs')
-rw-r--r-- | gnu/usr.bin/texinfo/emacs/Makefile.in | 94 | ||||
-rw-r--r-- | gnu/usr.bin/texinfo/emacs/detexinfo.el | 250 | ||||
-rw-r--r-- | gnu/usr.bin/texinfo/emacs/elisp-comp | 5 | ||||
-rw-r--r-- | gnu/usr.bin/texinfo/emacs/info.el | 1702 | ||||
-rw-r--r-- | gnu/usr.bin/texinfo/emacs/informat.el | 425 | ||||
-rw-r--r-- | gnu/usr.bin/texinfo/emacs/makeinfo.el | 245 | ||||
-rw-r--r-- | gnu/usr.bin/texinfo/emacs/texinfmt.el | 3067 | ||||
-rw-r--r-- | gnu/usr.bin/texinfo/emacs/texinfo.el | 757 | ||||
-rw-r--r-- | gnu/usr.bin/texinfo/emacs/texnfo-tex.el | 346 | ||||
-rw-r--r-- | gnu/usr.bin/texinfo/emacs/texnfo-upd.el | 2046 |
10 files changed, 8937 insertions, 0 deletions
diff --git a/gnu/usr.bin/texinfo/emacs/Makefile.in b/gnu/usr.bin/texinfo/emacs/Makefile.in new file mode 100644 index 00000000000..045b8bc0698 --- /dev/null +++ b/gnu/usr.bin/texinfo/emacs/Makefile.in @@ -0,0 +1,94 @@ +# Makefile for Texinfo/emacs. -*- Indented-Text -*- +# Copyright (C) 1995 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# Author: Brian J. Fox (bfox@ai.mit.edu) +# +#### Start of system configuration section. #### + +RM = rm -f +CP = cp + +srcdir = @srcdir@ +VPATH = $(srcdir) +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = $(exec_prefix)/bin +# Prefix for each installed program, normally empty or `g'. +binprefix = +libdir = $(prefix)/lib +# Prefix for each installed man page, normally empty or `g'. +manprefix = +mandir = $(prefix)/man/man1 +manext = 1 +infodir = $(prefix)/info +lispdir = $(libdir)/emacs/site-lisp + +#### End of system configuration section. #### + +ELISP_SRCS = info.el makeinfo.el texinfo.el texnfo-upd.el \ + texnfo-tex.el texinfmt.el informat.el detexinfo.el +ELISP_OBJS = info.elc makeinfo.elc texinfo.elc texnfo-upd.elc \ + texnfo-tex.elc texinfmt.elc informat.elc detexinfo.elc + +.SUFFIXES: .el .elc + +.el.elc: + $(srcdir)/elisp-comp $< + +all: $(ELISP_OBJS) +sub-all: all + +# install: $(ELISP_OBJS) +# @(echo "(print (car load-path))" >/tmp/elc.$$$$; \ +# lispdir=`emacs -batch -q -l /tmp/elc.$$$$ -nw | grep site-lisp`; \ +# rm /tmp/elc.$$$$; \ +# if [ "$$lispdir" != "" ]; then \ +# lispdir=`echo $$lispdir | sed -e 's/"//g'`; \ +# echo "Installing .elc files in $$lispdir."; \ +# $(CP) $(ELISP_OBJS) $$lispdir; \ +# else \ +# echo "To install the elisp files, please copy *.elc to the"; \ +# echo "emacs site-lisp directory."; \ +# fi) + +install: $(ELISP_OBJS) + for file in $(ELISP_OBJS); do \ + $(INSTALL_DATA) $$file $(lispdir); \ + done + +uninstall: $(ELISP_OBJS) + cd $(lispdir) && rm -f $(ELISP_OBJS) + +informat.elc: info.elc +makeinfo.elc: texinfo.elc +texinfmt.elc: texinfo.elc +texinfmt.elc: texnfo-upd.elc + +Makefile: $(srcdir)/Makefile.in ../config.status + cd ..; sh config.status + +realclean distclean: clean + $(RM) -f Makefile *.log + +clean: FORCE + $(RM) -f *.elc + +FORCE: + diff --git a/gnu/usr.bin/texinfo/emacs/detexinfo.el b/gnu/usr.bin/texinfo/emacs/detexinfo.el new file mode 100644 index 00000000000..fda99091c49 --- /dev/null +++ b/gnu/usr.bin/texinfo/emacs/detexinfo.el @@ -0,0 +1,250 @@ +;;; Here is a handy keybinding: + +(global-set-key "\C-x\\" 'detexinfo) + +;;;;;;;;;;;;;;;; detexinfo.el ;;;;;;;;;;;;;;;; +;;; +;;; Remove Texinfo commands from a Texinfo source file. +;;; +;;; Copyright (C) 1991, 1992 Free Software Foundation +;;; Robert J. Chassell +;;; bugs to bug-texinfo@prep.ai.mit.edu +;;; +;;; ==> test version <== +;;; Fails if Texinfo source file contains formatting errors. +;;; +;;; Version 0.05 - 3 Jun 1992 +;;; Add to list of removed commands. Improve messages. +;;; +;;; Version 0.04 - 27 Jan 1992 +;;; Rewrite to insert detexinfo'd text into a temporary buffer. +;;; +;;; Version 0.03 - 27 Dec 1991 +;;; Improved messages. +;;; +;;; Version 0.02 - 13 Nov 1991 +;;; detexinfo-remove-inline-cmd, detexinfo-syntax-table: Handle +;;; nested commands. +;;; detexinfo: Handle nested @'s, eg @samp{@}} and @samp{@@}; +;;; replace @TeX{} with TeX. +;;; +;;; Version 0.01 - 13 Nov 1991 +;;; +;;; Based on detex.el, by Bengt Martensson, 4 Oct 1987 +;;; +;;;;;;;;;;;;;;;; + +(defvar detexinfo-buffer-name "*detexinfo*" + "*Name of the temporary buffer used by \\[detexinfo].") + +(defvar detexinfo-syntax-table nil) + +(if detexinfo-syntax-table + nil + (setq detexinfo-syntax-table (make-syntax-table)) + (modify-syntax-entry ?\[ "." detexinfo-syntax-table) + (modify-syntax-entry ?\] "." detexinfo-syntax-table) + (modify-syntax-entry ?\" "." detexinfo-syntax-table) + (modify-syntax-entry ?\\ "." detexinfo-syntax-table) + (modify-syntax-entry ?\( "." detexinfo-syntax-table) + (modify-syntax-entry ?\) "." detexinfo-syntax-table) + (modify-syntax-entry ?{ "(}" detexinfo-syntax-table) + (modify-syntax-entry ?} "){" detexinfo-syntax-table)) + +(defun detexinfo () + "Remove Texinfo commands from current buffer, copying result to new buffer. +BUG: Fails if Texinfo source file contains formatting errors." + (interactive) + (let ((input-buffer (current-buffer))) + ;; Find a buffer to use. + (switch-to-buffer (get-buffer-create detexinfo-buffer-name)) + (setq major-mode 'detexinfo-mode) + (set-syntax-table detexinfo-syntax-table) + (erase-buffer) + (insert-buffer-substring input-buffer) + + ;; Replace @{ and @} with %#* and *#% temporarily, so @samp{@{} works. + ;; What is a better way of doing this?? + (goto-char (point-min)) + (while (search-forward "@{" nil t) ; e.g., @samp{@{} + (replace-match "%#*")) + (goto-char (point-min)) + (while (search-forward "@}" nil t) + (forward-char -3) ; e.g., @samp{@@} + (if (looking-at "@") ; Two @@ in a row + (progn + (delete-char 2) + (insert "%&%#")) + (forward-char 1) + (delete-char 2) + (insert "*#%"))) + + (goto-char (point-min)) + ;; Remove @refill, the only inline command without braces. + (while (search-forward "@refill" nil t) + (replace-match "")) + ;; Replace @TeX{} with TeX + (goto-char (point-min)) + (while (search-forward "@TeX{}" nil t) (replace-match "TeX" t t)) + + (detexinfo-remove-line-cmds-without-arg) + (detexinfo-remove-inline-cmds-without-arg) + (detexinfo-remove-inline-cmds-keep-arg) + (detexinfo-remove-line-cmds-deletable-arg) + (detexinfo-remove-line-cmds-maybe-delete-arg) + (detexinfo-remove-line-cmds-keep-arg) + + ;; Now replace %#*, *#%, and %&%# with {, }, and @@. + (goto-char (point-min)) + (while (search-forward "%#*" nil t) + (replace-match "{")) + (goto-char (point-min)) + (while (search-forward "*#%" nil t) + (replace-match "}")) + (goto-char (point-min)) + (while (search-forward "%&%#" nil t) + (replace-match "@@")) + + ;; Scan for remaining two character @-commands + (goto-char (point-min)) + (while (search-forward "@" nil t) + (cond ((looking-at "[*:]") + (delete-region (1- (point)) (1+ (point)))) + ((looking-at "[{}^@.'`]\"?!") + (delete-region (1- (point)) (point))))) + + (goto-char (point-min)) + (message "Done...removed Texinfo commands from buffer. You may save it."))) + +(defun detexinfo-remove-whole-line (cmd) + "Delete Texinfo line command CMD at beginning of line and rest of line." + (goto-char (point-min)) + (while + (re-search-forward + (concat "^@" cmd "[ \n]+") (point-max) t) + (goto-char (match-beginning 0)) + (delete-region + (point) (save-excursion (end-of-line) (1+ (point)))))) + +(defun detexinfo-remove-inline-cmd (cmd) + "Delete Texinfo inline command CMD, eg. @point, @code." + (goto-char (point-min)) + (while + (re-search-forward (concat "@" cmd "{") (point-max) t) + (save-excursion + (forward-char -1) + (forward-sexp 1) + (delete-char -1)) ; delete right brace + (delete-region (point) (match-beginning 0)))) + +;;;;;;;;;;;;;;;; + +;;; 1. @setfilename and other line commands with args to delete + +(defvar detexinfo-line-cmds-deletable-arg + '("enumerate" "ftable" "vtable" "itemize" "table" + "setfilename" "settitle" "setchapternewpage" + "footnotestyle" "paragraphindent" + "include" "need" "sp" + "clear" "ifclear" "ifset" "set" + "defcodeindex" "defindex" "syncodeindex" "synindex") + "List of Texinfo commands whose arguments should be deleted.") + +(defun detexinfo-remove-line-cmds-deletable-arg () + "Delete Texinfo line commands together with their args, eg @setfilename." + (message "Removing commands such as @enumerate...with their arguments...") + (mapcar 'detexinfo-remove-whole-line + detexinfo-line-cmds-deletable-arg)) + +;;; 2. @cindex and other cmds with args that may be deleted +;;; This list is here just to make it easier to revise the +;;; categories. In particular, you might want to keep the index entries. + +(defvar detexinfo-line-cmds-maybe-delete-arg + '("cindex" "findex" "kindex" "pindex" "tindex" "vindex" "node" + "c" "comment" "end" "headings" "printindex" "vskip" + "evenfooting" "evenheading" "everyfooting" "everyheading" + "oddfooting" "oddheading") + "List of Texinfo commands whose arguments may possibly be deleted.") + +(defun detexinfo-remove-line-cmds-maybe-delete-arg () + "Delete Texinfo line commands together with their arguments, eg, @cindex." + (message "Removing commands such as @cindex...with their arguments...") + (mapcar 'detexinfo-remove-whole-line + detexinfo-line-cmds-maybe-delete-arg)) + +;;; 3. @chapter and other line cmds with args to keep. + +(defvar detexinfo-line-cmds-keep-arg + '("top" "chapter" "section" "subsection" "subsubsection" + "unnumbered" "unnumberedsec" "unnumberedsubsec" "unnumberedsubsubsec" + "majorheading" "chapheading" "heading" "subheading" "subsubheading" + "appendix" "appendixsec" "appendixsubsec" "appendixsubsubsec" + "item" "itemx" + "title" "subtitle" "center" "author" "exdent" + "defcv" "deffn" "defivar" "defmac" "defmethod" "defop" "defopt" + "defspec" "deftp" "deftypefn" "deftypefun" "deftypvr" + "deftypevar" "defun" "defvar" "defvr") + "List of Texinfo line commands whose arguments should be kept.") + +(defun detexinfo-remove-line-cmds-keep-arg () + "Delete Texinfo line commands but keep their arguments, eg @chapter." + (message "Removing commands such as @chapter...but not their arguments...") + (mapcar 'detexinfo-remove-line-cmd-keep-arg + detexinfo-line-cmds-keep-arg)) + +(defun detexinfo-remove-line-cmd-keep-arg (cmd) + "Delete Texinfo line command CMD but keep its argument, eg @chapter." + (goto-char (point-min)) + (while + (re-search-forward + (concat "^@" cmd "[ \n]+") (point-max) t) + (delete-region (match-beginning 0) (match-end 0)))) + +;;; 4. @bye and other line commands without args. + +(defvar detexinfo-line-cmds-without-arg + '("bye" "contents" "display" "example" "finalout" + "flushleft" "flushright" "format" "group" "ifhtml" "ifinfo" "iftex" + "ignore" "lisp" "menu" "noindent" "page" "quotation" + "shortcontents" "smallbook" "smallexample" "smalllisp" + "summarycontents" "tex" "thischapter" "thischaptername" + "thisfile" "thispage" "thissection" "thistitle" "titlepage") + "List of Texinfo commands without arguments that should be deleted.") + +(defun detexinfo-remove-line-cmds-without-arg () + "Delete line Texinfo commands that lack args, eg. @example." + (message "Removing commands such as @example...that lack arguments...") + (mapcar 'detexinfo-remove-whole-line + detexinfo-line-cmds-without-arg)) + +;;; 5. @equiv and other inline cmds without args. + +(defvar detexinfo-inline-cmds-without-arg + '("equiv" "error" "expansion" "point" "print" "result" + "asis" "br" "bullet" "dots" "minus" "today") + "List of Texinfo inline commands without arguments that should be deleted.") + +(defun detexinfo-remove-inline-cmds-without-arg () + "Delete Texinfo inline commands in that lack arguments." + (message "Removing within line commands such as @result...") + (mapcar 'detexinfo-remove-inline-cmd + detexinfo-inline-cmds-without-arg)) + +;;; 6. @code and other inline cmds with args to keep + +(defvar detexinfo-inline-cmds-keep-arg + '("b" "cartouche" "cite" "code" "copyright" "ctrl" "dfn" "dmn" + "emph" "file" "footnote" "i" "inforef" + "kbd" "key" "pxref" "r" "ref" "samp" "sc" "titlefont" + "strong" "t" "var" "w" "xref") + "List of Texinfo inline commands with arguments that should be kept.") + +(defun detexinfo-remove-inline-cmds-keep-arg () + "Delete Texinfo inline commands but keep its arg, eg. @code." + (message + "Removing within line commands such as @code...but not their arguments...") + (mapcar 'detexinfo-remove-inline-cmd + detexinfo-inline-cmds-keep-arg)) + +;;;;;;;;;;;;;;;; end detexinfo.el ;;;;;;;;;;;;;;;; diff --git a/gnu/usr.bin/texinfo/emacs/elisp-comp b/gnu/usr.bin/texinfo/emacs/elisp-comp new file mode 100644 index 00000000000..8f27d7bde8f --- /dev/null +++ b/gnu/usr.bin/texinfo/emacs/elisp-comp @@ -0,0 +1,5 @@ +#!/bin/sh +setpath=/tmp/elc.$$ +echo "(setq load-path (cons nil load-path))" > $setpath +emacs -batch -l $setpath -f batch-byte-compile $* +rm -f $setpath diff --git a/gnu/usr.bin/texinfo/emacs/info.el b/gnu/usr.bin/texinfo/emacs/info.el new file mode 100644 index 00000000000..46c53a86ed8 --- /dev/null +++ b/gnu/usr.bin/texinfo/emacs/info.el @@ -0,0 +1,1702 @@ +;;; info.el --- info package for Emacs. + +;; Copyright (C) 1985, 1986, 1992, 1993, 1994 Free Software Foundation, Inc. + +;; Maintainer: FSF +;; Keywords: help + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +;;; Commentary: + +;;; Note that nowadays we expect info files to be made using makeinfo. + +;;; Code: + +(defvar Info-history nil + "List of info nodes user has visited. +Each element of list is a list (FILENAME NODENAME BUFFERPOS).") + +(defvar Info-enable-edit nil + "*Non-nil means the \\<Info-mode-map>\\[Info-edit] command in Info can edit the current node. +This is convenient if you want to write info files by hand. +However, we recommend that you not do this. +It is better to write a Texinfo file and generate the Info file from that, +because that gives you a printed manual as well.") + +(defvar Info-enable-active-nodes t + "Non-nil allows Info to execute Lisp code associated with nodes. +The Lisp code is executed when the node is selected.") + +(defvar Info-default-directory-list nil + "List of default directories to search for Info documentation files. +This value is used as the default for `Info-directory-list'. It is set +in paths.el.") + +(defvar Info-fontify t + "*Non-nil enables highlighting and fonts in Info nodes.") + +(defvar Info-fontify-maximum-menu-size 30000 + "*Maximum size of menu to fontify if `Info-fontify' is non-nil.") + +(defvar Info-directory-list + (let ((path (getenv "INFOPATH")) + ;; This is for older Emacs versions + ;; which might get this info.el from the Texinfo distribution. + (path-separator (if (boundp 'path-separator) path-separator + (if (eq system-type 'ms-dos) ";" ":"))) + (sibling (if installation-directory + (expand-file-name "info/" installation-directory)))) + (if path + (let ((list nil) + idx) + (while (> (length path) 0) + (setq idx (or (string-match path-separator path) (length path)) + list (cons (substring path 0 idx) list) + path (substring path (min (1+ idx) + (length path))))) + (nreverse list)) + (if (or (null sibling) + (member sibling Info-default-directory-list) + (not (file-exists-p sibling)) + ;; On DOS/NT, we use movable executables always, + ;; and we must always find the Info dir at run time. + (if (or (eq system-type 'ms-dos) (eq system-type 'windows-nt)) + nil + ;; Use invocation-directory for Info only if we used it for + ;; exec-directory also. + (not (string= exec-directory + (expand-file-name "lib-src/" + installation-directory))))) + Info-default-directory-list + (reverse (cons sibling (cdr (reverse Info-default-directory-list))))))) + "List of directories to search for Info documentation files. +nil means not yet initialized. In this case, Info uses the environment +variable INFOPATH to initialize it, or `Info-default-directory-list' +if there is no INFOPATH variable in the environment. +The last element of `Info-default-directory-list' is the directory +where Emacs installs the Info files that come with it. + +If you run the Emacs executable from the `src' directory in the Emacs +source tree, the `info' directory in the source tree is used as the last +element, in place of the installation Info directory. This is useful +when you run a version of Emacs without installing it.") + +(defvar Info-additional-directory-list nil + "List of additional directories to search for Info documentation files. +These directories are not searched for merging the `dir' file.") + +(defvar Info-current-file nil + "Info file that Info is now looking at, or nil.") + +(defvar Info-current-subfile nil + "Info subfile that is actually in the *info* buffer now, +or nil if current info file is not split into subfiles.") + +(defvar Info-current-node nil + "Name of node that Info is now looking at, or nil.") + +(defvar Info-tag-table-marker (make-marker) + "Marker pointing at beginning of current Info file's tag table. +Marker points nowhere if file has no tag table.") + +(defvar Info-current-file-completions nil + "Cached completion list for current Info file.") + +(defvar Info-index-alternatives nil + "List of possible matches for last Info-index command.") + +(defvar Info-standalone nil + "Non-nil if Emacs was started solely as an Info browser.") + +(defvar Info-suffix-list '( (".info.Z" . "uncompress") + (".info.Y" . "unyabba") + (".info.gz" . "gunzip") + (".info.z" . "gunzip") + (".info" . nil) + (".Z" . "uncompress") + (".Y" . "unyabba") + (".gz" . "gunzip") + (".z" . "gunzip") + ("" . nil)) + "List of file name suffixes and associated decoding commands. +Each entry should be (SUFFIX . STRING); the file is given to +the command as standard input. If STRING is nil, no decoding is done. +Because the SUFFIXes are tried in order, the empty string should +be last in the list.") + +(defun info-insert-file-contents (filename &optional visit) + "Insert the contents of an info file in the current buffer. +Do the right thing if the file has been compressed or zipped." + (let ((tail Info-suffix-list) + fullname decoder) + (if (file-exists-p filename) + (progn + (while (and tail + (not (string-match + (concat (regexp-quote (car (car tail))) "$") + filename))) + (setq tail (cdr tail))) + (setq fullname filename + decoder (cdr (car tail)))) + (while (and tail + (not (file-exists-p (concat filename (car (car tail)))))) + (setq tail (cdr tail))) + (setq fullname (concat filename (car (car tail))) + decoder (cdr (car tail))) + (or tail + (error "Can't find %s or any compressed version of it!" filename))) + ;; check for conflict with jka-compr + (if (and (featurep 'jka-compr) + (jka-compr-installed-p) + (jka-compr-get-compression-info fullname)) + (setq decoder nil)) + (insert-file-contents fullname visit) + (if decoder + (let ((buffer-read-only nil) + (default-directory (or (file-name-directory fullname) + default-directory))) + (shell-command-on-region (point-min) (point-max) decoder t))))) + +;;;###autoload (add-hook 'same-window-buffer-names "*info*") + +;;;###autoload +(defun info (&optional file) + "Enter Info, the documentation browser. +Optional argument FILE specifies the file to examine; +the default is the top-level directory of Info. + +In interactive use, a prefix argument directs this command +to read a file name from the minibuffer." + (interactive (if current-prefix-arg + (list (read-file-name "Info file name: " nil nil t)))) + (if file + (Info-goto-node (concat "(" file ")")) + (if (get-buffer "*info*") + (pop-to-buffer "*info*") + (Info-directory)))) + +;;;###autoload +(defun info-standalone () + "Run Emacs as a standalone Info reader. +Usage: emacs -f info-standalone [filename] +In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself." + (setq Info-standalone t) + (if (and command-line-args-left + (not (string-match "^-" (car command-line-args-left)))) + (condition-case err + (progn + (info (car command-line-args-left)) + (setq command-line-args-left (cdr command-line-args-left))) + (error (send-string-to-terminal + (format "%s\n" (if (eq (car-safe err) 'error) + (nth 1 err) err))) + (save-buffers-kill-emacs))) + (info))) + +;; Go to an info node specified as separate filename and nodename. +;; no-going-back is non-nil if recovering from an error in this function; +;; it says do not attempt further (recursive) error recovery. +(defun Info-find-node (filename nodename &optional no-going-back) + ;; Convert filename to lower case if not found as specified. + ;; Expand it. + (if filename + (let (temp temp-downcase found) + (setq filename (substitute-in-file-name filename)) + (if (string= (downcase (file-name-nondirectory filename)) "dir") + (setq found t) + (let ((dirs (if (string-match "^\\./" filename) + ;; If specified name starts with `./' + ;; then just try current directory. + '("./") + (if (file-name-absolute-p filename) + ;; No point in searching for an + ;; absolute file name + '(nil) + (if Info-additional-directory-list + (append Info-directory-list + Info-additional-directory-list) + Info-directory-list))))) + ;; Search the directory list for file FILENAME. + (while (and dirs (not found)) + (setq temp (expand-file-name filename (car dirs))) + (setq temp-downcase + (expand-file-name (downcase filename) (car dirs))) + ;; Try several variants of specified name. + (let ((suffix-list Info-suffix-list)) + (while (and suffix-list (not found)) + (cond ((file-exists-p + (concat temp (car (car suffix-list)))) + (setq found temp)) + ((file-exists-p + (concat temp-downcase (car (car suffix-list)))) + (setq found temp-downcase))) + (setq suffix-list (cdr suffix-list)))) + (setq dirs (cdr dirs))))) + (if found + (setq filename found) + (error "Info file %s does not exist" filename)))) + ;; Record the node we are leaving. + (if (and Info-current-file (not no-going-back)) + (setq Info-history + (cons (list Info-current-file Info-current-node (point)) + Info-history))) + ;; Go into info buffer. + (switch-to-buffer "*info*") + (buffer-disable-undo (current-buffer)) + (or (eq major-mode 'Info-mode) + (Info-mode)) + (widen) + (setq Info-current-node nil) + (unwind-protect + (progn + ;; Switch files if necessary + (or (null filename) + (equal Info-current-file filename) + (let ((buffer-read-only nil)) + (setq Info-current-file nil + Info-current-subfile nil + Info-current-file-completions nil + Info-index-alternatives nil + buffer-file-name nil) + (erase-buffer) + (if (eq filename t) + (Info-insert-dir) + (info-insert-file-contents filename t) + (setq default-directory (file-name-directory filename))) + (set-buffer-modified-p nil) + ;; See whether file has a tag table. Record the location if yes. + (set-marker Info-tag-table-marker nil) + (goto-char (point-max)) + (forward-line -8) + ;; Use string-equal, not equal, to ignore text props. + (or (string-equal nodename "*") + (not (search-forward "\^_\nEnd tag table\n" nil t)) + (let (pos) + ;; We have a tag table. Find its beginning. + ;; Is this an indirect file? + (search-backward "\nTag table:\n") + (setq pos (point)) + (if (save-excursion + (forward-line 2) + (looking-at "(Indirect)\n")) + ;; It is indirect. Copy it to another buffer + ;; and record that the tag table is in that buffer. + (save-excursion + (let ((buf (current-buffer))) + (set-buffer (get-buffer-create " *info tag table*")) + (buffer-disable-undo (current-buffer)) + (setq case-fold-search t) + (erase-buffer) + (insert-buffer-substring buf) + (set-marker Info-tag-table-marker + (match-end 0)))) + (set-marker Info-tag-table-marker pos)))) + (setq Info-current-file + (if (eq filename t) "dir" + (file-name-sans-versions buffer-file-name))))) + ;; Use string-equal, not equal, to ignore text props. + (if (string-equal nodename "*") + (progn (setq Info-current-node nodename) + (Info-set-mode-line)) + ;; Search file for a suitable node. + (let ((guesspos (point-min)) + (regexp (concat "Node: *" (regexp-quote nodename) " *[,\t\n\177]"))) + ;; First get advice from tag table if file has one. + ;; Also, if this is an indirect info file, + ;; read the proper subfile into this buffer. + (if (marker-position Info-tag-table-marker) + (save-excursion + (set-buffer (marker-buffer Info-tag-table-marker)) + (goto-char Info-tag-table-marker) + (if (re-search-forward regexp nil t) + (progn + (setq guesspos (read (current-buffer))) + ;; If this is an indirect file, + ;; determine which file really holds this node + ;; and read it in. + (if (not (eq (current-buffer) (get-buffer "*info*"))) + (setq guesspos + (Info-read-subfile guesspos)))) + (error "No such node: \"%s\"" nodename)))) + (goto-char (max (point-min) (- guesspos 1000))) + ;; Now search from our advised position (or from beg of buffer) + ;; to find the actual node. + (catch 'foo + (while (search-forward "\n\^_" nil t) + (forward-line 1) + (let ((beg (point))) + (forward-line 1) + (if (re-search-backward regexp beg t) + (throw 'foo t)))) + (error "No such node: %s" nodename))) + (Info-select-node))) + ;; If we did not finish finding the specified node, + ;; go back to the previous one. + (or Info-current-node no-going-back (null Info-history) + (let ((hist (car Info-history))) + (setq Info-history (cdr Info-history)) + (Info-find-node (nth 0 hist) (nth 1 hist) t) + (goto-char (nth 2 hist))))) + (goto-char (point-min))) + +;; Cache the contents of the (virtual) dir file, once we have merged +;; it for the first time, so we can save time subsequently. +(defvar Info-dir-contents nil) + +;; Cache for the directory we decided to use for the default-directory +;; of the merged dir text. +(defvar Info-dir-contents-directory nil) + +;; Record the file attributes of all the files from which we +;; constructed Info-dir-contents. +(defvar Info-dir-file-attributes nil) + +;; Construct the Info directory node by merging the files named `dir' +;; from various directories. Set the *info* buffer's +;; default-directory to the first directory we actually get any text +;; from. +(defun Info-insert-dir () + (if (and Info-dir-contents Info-dir-file-attributes + ;; Verify that none of the files we used has changed + ;; since we used it. + (eval (cons 'and + (mapcar '(lambda (elt) + (let ((curr (file-attributes (car elt)))) + ;; Don't compare the access time. + (if curr (setcar (nthcdr 4 curr) 0)) + (setcar (nthcdr 4 (cdr elt)) 0) + (equal (cdr elt) curr))) + Info-dir-file-attributes)))) + (insert Info-dir-contents) + (let ((dirs Info-directory-list) + buffers buffer others nodes dirs-done) + + (setq Info-dir-file-attributes nil) + + ;; Search the directory list for the directory file. + (while dirs + (let ((truename (file-truename (expand-file-name (car dirs))))) + (or (member truename dirs-done) + (member (directory-file-name truename) dirs-done) + ;; Try several variants of specified name. + ;; Try upcasing, appending `.info', or both. + (let* (file + (attrs + (or + (progn (setq file (expand-file-name "dir" truename)) + (file-attributes file)) + (progn (setq file (expand-file-name "DIR" truename)) + (file-attributes file)) + (progn (setq file (expand-file-name "dir.info" truename)) + (file-attributes file)) + (progn (setq file (expand-file-name "DIR.INFO" truename)) + (file-attributes file))))) + (setq dirs-done + (cons truename + (cons (directory-file-name truename) + dirs-done))) + (if attrs + (save-excursion + (or buffers + (message "Composing main Info directory...")) + (set-buffer (generate-new-buffer "info dir")) + (insert-file-contents file) + (setq buffers (cons (current-buffer) buffers) + Info-dir-file-attributes + (cons (cons file attrs) + Info-dir-file-attributes)))))) + (setq dirs (cdr dirs)))) + + (or buffers + (error "Can't find the info directory node")) + ;; Distinguish the dir file that comes with Emacs from all the + ;; others. Yes, that is really what this is supposed to do. + ;; If it doesn't work, fix it. + (setq buffer (car buffers) + others (cdr buffers)) + + ;; Insert the entire original dir file as a start; use its + ;; default directory as the default directory for the whole + ;; concatenation. + (insert-buffer buffer) + (setq Info-dir-contents-directory (save-excursion + (set-buffer buffer) + default-directory)) + + ;; Look at each of the other buffers one by one. + (while others + (let ((other (car others))) + ;; In each, find all the menus. + (save-excursion + (set-buffer other) + (goto-char (point-min)) + ;; Find each menu, and add an elt to NODES for it. + (while (re-search-forward "^\\* Menu:" nil t) + (let (beg nodename end) + (forward-line 1) + (setq beg (point)) + (search-backward "\n\^_") + (search-forward "Node: ") + (setq nodename (Info-following-node-name)) + (search-forward "\n\^_" nil 'move) + (beginning-of-line) + (setq end (point)) + (setq nodes (cons (list nodename other beg end) nodes)))))) + (setq others (cdr others))) + ;; Add to the main menu a menu item for each other node. + (re-search-forward "^\\* Menu:") + (forward-line 1) + (let ((menu-items '("top")) + (nodes nodes) + (case-fold-search t) + (end (save-excursion (search-forward "\^_" nil t) (point)))) + (while nodes + (let ((nodename (car (car nodes)))) + (save-excursion + (or (member (downcase nodename) menu-items) + (re-search-forward (concat "^\\* " + (regexp-quote nodename) + "::") + end t) + (progn + (insert "* " nodename "::" "\n") + (setq menu-items (cons nodename menu-items)))))) + (setq nodes (cdr nodes)))) + ;; Now take each node of each of the other buffers + ;; and merge it into the main buffer. + (while nodes + (let ((nodename (car (car nodes)))) + (goto-char (point-min)) + ;; Find the like-named node in the main buffer. + (if (re-search-forward (concat "\n\^_.*\n.*Node: " + (regexp-quote nodename) + "[,\n\t]") + nil t) + (progn + (search-forward "\n\^_" nil 'move) + (beginning-of-line) + (insert "\n")) + ;; If none exists, add one. + (goto-char (point-max)) + (insert "\^_\nFile: dir\tNode: " nodename "\n\n* Menu:\n\n")) + ;; Merge the text from the other buffer's menu + ;; into the menu in the like-named node in the main buffer. + (apply 'insert-buffer-substring (cdr (car nodes)))) + (setq nodes (cdr nodes))) + ;; Kill all the buffers we just made. + (while buffers + (kill-buffer (car buffers)) + (setq buffers (cdr buffers))) + (message "Composing main Info directory...done")) + (setq Info-dir-contents (buffer-string))) + (setq default-directory Info-dir-contents-directory)) + +(defun Info-read-subfile (nodepos) + (set-buffer (marker-buffer Info-tag-table-marker)) + (goto-char (point-min)) + (search-forward "\n\^_") + (let (lastfilepos + lastfilename) + (forward-line 2) + (catch 'foo + (while (not (looking-at "\^_")) + (if (not (eolp)) + (let ((beg (point)) + thisfilepos thisfilename) + (search-forward ": ") + (setq thisfilename (buffer-substring beg (- (point) 2))) + (setq thisfilepos (read (current-buffer))) + ;; read in version 19 stops at the end of number. + ;; Advance to the next line. + (forward-line 1) + (if (> thisfilepos nodepos) + (throw 'foo t)) + (setq lastfilename thisfilename) + (setq lastfilepos thisfilepos)) + (forward-line 1)))) + (set-buffer (get-buffer "*info*")) + (or (equal Info-current-subfile lastfilename) + (let ((buffer-read-only nil)) + (setq buffer-file-name nil) + (widen) + (erase-buffer) + (info-insert-file-contents lastfilename) + (set-buffer-modified-p nil) + (setq Info-current-subfile lastfilename))) + (goto-char (point-min)) + (search-forward "\n\^_") + (+ (- nodepos lastfilepos) (point)))) + +;; Select the info node that point is in. +(defun Info-select-node () + (save-excursion + ;; Find beginning of node. + (search-backward "\n\^_") + (forward-line 2) + ;; Get nodename spelled as it is in the node. + (re-search-forward "Node:[ \t]*") + (setq Info-current-node + (buffer-substring (point) + (progn + (skip-chars-forward "^,\t\n") + (point)))) + (Info-set-mode-line) + ;; Find the end of it, and narrow. + (beginning-of-line) + (let (active-expression) + (narrow-to-region (point) + (if (re-search-forward "\n[\^_\f]" nil t) + (prog1 + (1- (point)) + (if (looking-at "[\n\^_\f]*execute: ") + (progn + (goto-char (match-end 0)) + (setq active-expression + (read (current-buffer)))))) + (point-max))) + (if Info-enable-active-nodes (eval active-expression)) + (if Info-fontify (Info-fontify-node)) + (run-hooks 'Info-selection-hook)))) + +(defun Info-set-mode-line () + (setq mode-line-buffer-identification + (concat + "Info: (" + (if Info-current-file + (file-name-nondirectory Info-current-file) + "") + ")" + (or Info-current-node "")))) + +;; Go to an info node specified with a filename-and-nodename string +;; of the sort that is found in pointers in nodes. + +(defun Info-goto-node (nodename) + "Go to info node named NAME. Give just NODENAME or (FILENAME)NODENAME." + (interactive (list (Info-read-node-name "Goto node: "))) + (let (filename) + (string-match "\\s *\\((\\s *\\([^\t)]*\\)\\s *)\\s *\\|\\)\\(.*\\)" + nodename) + (setq filename (if (= (match-beginning 1) (match-end 1)) + "" + (substring nodename (match-beginning 2) (match-end 2))) + nodename (substring nodename (match-beginning 3) (match-end 3))) + (let ((trim (string-match "\\s *\\'" filename))) + (if trim (setq filename (substring filename 0 trim)))) + (let ((trim (string-match "\\s *\\'" nodename))) + (if trim (setq nodename (substring nodename 0 trim)))) + (Info-find-node (if (equal filename "") nil filename) + (if (equal nodename "") "Top" nodename)))) + +(defun Info-read-node-name (prompt &optional default) + (let* ((completion-ignore-case t) + (nodename (completing-read prompt (Info-build-node-completions)))) + (if (equal nodename "") + (or default + (Info-read-node-name prompt)) + nodename))) + +(defun Info-build-node-completions () + (or Info-current-file-completions + (let ((compl nil)) + (save-excursion + (save-restriction + (if (marker-buffer Info-tag-table-marker) + (progn + (set-buffer (marker-buffer Info-tag-table-marker)) + (widen) + (goto-char Info-tag-table-marker) + (while (re-search-forward "\nNode: \\(.*\\)\177" nil t) + (setq compl + (cons (list (buffer-substring (match-beginning 1) + (match-end 1))) + compl)))) + (widen) + (goto-char (point-min)) + (while (search-forward "\n\^_" nil t) + (forward-line 1) + (let ((beg (point))) + (forward-line 1) + (if (re-search-backward "Node: *\\([^,\n]*\\) *[,\n\t]" + beg t) + (setq compl + (cons (list (buffer-substring (match-beginning 1) + (match-end 1))) + compl)))))))) + (setq Info-current-file-completions compl)))) + +(defun Info-restore-point (hl) + "If this node has been visited, restore the point value when we left." + (while hl + (if (and (equal (nth 0 (car hl)) Info-current-file) + ;; Use string-equal, not equal, to ignore text props. + (string-equal (nth 1 (car hl)) Info-current-node)) + (progn + (goto-char (nth 2 (car hl))) + (setq hl nil)) ;terminate the while at next iter + (setq hl (cdr hl))))) + +(defvar Info-last-search nil + "Default regexp for \\<Info-mode-map>\\[Info-search] command to search for.") + +(defun Info-search (regexp) + "Search for REGEXP, starting from point, and select node it's found in." + (interactive "sSearch (regexp): ") + (if (equal regexp "") + (setq regexp Info-last-search) + (setq Info-last-search regexp)) + (let ((found ()) current + (onode Info-current-node) + (ofile Info-current-file) + (opoint (point)) + (osubfile Info-current-subfile)) + (save-excursion + (save-restriction + (widen) + (if (null Info-current-subfile) + (progn (re-search-forward regexp) (setq found (point))) + (condition-case err + (progn (re-search-forward regexp) (setq found (point))) + (search-failed nil))))) + (if (not found) ;can only happen in subfile case -- else would have erred + (unwind-protect + (let ((list ())) + (set-buffer (marker-buffer Info-tag-table-marker)) + (goto-char (point-min)) + (search-forward "\n\^_\nIndirect:") + (save-restriction + (narrow-to-region (point) + (progn (search-forward "\n\^_") + (1- (point)))) + (goto-char (point-min)) + (search-forward (concat "\n" osubfile ": ")) + (beginning-of-line) + (while (not (eobp)) + (re-search-forward "\\(^.*\\): [0-9]+$") + (goto-char (+ (match-end 1) 2)) + (setq list (cons (cons (read (current-buffer)) + (buffer-substring (match-beginning 1) + (match-end 1))) + list)) + (goto-char (1+ (match-end 0)))) + (setq list (nreverse list) + current (car (car list)) + list (cdr list))) + (while list + (message "Searching subfile %s..." (cdr (car list))) + (Info-read-subfile (car (car list))) + (setq list (cdr list)) +;; (goto-char (point-min)) + (if (re-search-forward regexp nil t) + (setq found (point) list ()))) + (if found + (message "") + (signal 'search-failed (list regexp)))) + (if (not found) + (progn (Info-read-subfile opoint) + (goto-char opoint) + (Info-select-node))))) + (widen) + (goto-char found) + (Info-select-node) + ;; Use string-equal, not equal, to ignore text props. + (or (and (string-equal onode Info-current-node) + (equal ofile Info-current-file)) + (setq Info-history (cons (list ofile onode opoint) + Info-history))))) + +;; Extract the value of the node-pointer named NAME. +;; If there is none, use ERRORNAME in the error message; +;; if ERRORNAME is nil, just return nil. +(defun Info-extract-pointer (name &optional errorname) + (save-excursion + (goto-char (point-min)) + (forward-line 1) + (if (re-search-backward (concat name ":") nil t) + (progn + (goto-char (match-end 0)) + (Info-following-node-name)) + (if (eq errorname t) + nil + (error (concat "Node has no " (capitalize (or errorname name)))))))) + +;; Return the node name in the buffer following point. +;; ALLOWEDCHARS, if non-nil, goes within [...] to make a regexp +;; saying which chas may appear in the node name. +(defun Info-following-node-name (&optional allowedchars) + (skip-chars-forward " \t") + (buffer-substring + (point) + (progn + (while (looking-at (concat "[" (or allowedchars "^,\t\n") "]")) + (skip-chars-forward (concat (or allowedchars "^,\t\n") "(")) + (if (looking-at "(") + (skip-chars-forward "^)"))) + (skip-chars-backward " ") + (point)))) + +(defun Info-next () + "Go to the next node of this node." + (interactive) + (Info-goto-node (Info-extract-pointer "next"))) + +(defun Info-prev () + "Go to the previous node of this node." + (interactive) + (Info-goto-node (Info-extract-pointer "prev[ious]*" "previous"))) + +(defun Info-up () + "Go to the superior node of this node." + (interactive) + (Info-goto-node (Info-extract-pointer "up")) + (Info-restore-point Info-history)) + +(defun Info-last () + "Go back to the last node visited." + (interactive) + (or Info-history + (error "This is the first Info node you looked at")) + (let (filename nodename opoint) + (setq filename (car (car Info-history))) + (setq nodename (car (cdr (car Info-history)))) + (setq opoint (car (cdr (cdr (car Info-history))))) + (setq Info-history (cdr Info-history)) + (Info-find-node filename nodename) + (setq Info-history (cdr Info-history)) + (goto-char opoint))) + +(defun Info-directory () + "Go to the Info directory node." + (interactive) + (Info-find-node "dir" "top")) + +(defun Info-follow-reference (footnotename) + "Follow cross reference named NAME to the node it refers to. +NAME may be an abbreviation of the reference name." + (interactive + (let ((completion-ignore-case t) + completions default alt-default (start-point (point)) str i bol eol) + (save-excursion + ;; Store end and beginning of line. + (end-of-line) + (setq eol (point)) + (beginning-of-line) + (setq bol (point)) + + (goto-char (point-min)) + (while (re-search-forward "\\*note[ \n\t]*\\([^:]*\\):" nil t) + (setq str (buffer-substring + (match-beginning 1) + (1- (point)))) + ;; See if this one should be the default. + (and (null default) + (<= (match-beginning 0) start-point) + (<= start-point (point)) + (setq default t)) + ;; See if this one should be the alternate default. + (and (null alt-default) + (and (<= bol (match-beginning 0)) + (<= (point) eol)) + (setq alt-default t)) + (setq i 0) + (while (setq i (string-match "[ \n\t]+" str i)) + (setq str (concat (substring str 0 i) " " + (substring str (match-end 0)))) + (setq i (1+ i))) + ;; Record as a completion and perhaps as default. + (if (eq default t) (setq default str)) + (if (eq alt-default t) (setq alt-default str)) + (setq completions + (cons (cons str nil) + completions)))) + ;; If no good default was found, try an alternate. + (or default + (setq default alt-default)) + ;; If only one cross-reference found, then make it default. + (if (eq (length completions) 1) + (setq default (car (car completions)))) + (if completions + (let ((input (completing-read (if default + (concat "Follow reference named: (" + default ") ") + "Follow reference named: ") + completions nil t))) + (list (if (equal input "") + default input))) + (error "No cross-references in this node")))) + (let (target beg i (str (concat "\\*note " (regexp-quote footnotename)))) + (while (setq i (string-match " " str i)) + (setq str (concat (substring str 0 i) "[ \t\n]+" (substring str (1+ i)))) + (setq i (+ i 6))) + (save-excursion + (goto-char (point-min)) + (or (re-search-forward str nil t) + (error "No cross-reference named %s" footnotename)) + (goto-char (+ (match-beginning 0) 5)) + (setq target + (Info-extract-menu-node-name "Bad format cross reference" t))) + (while (setq i (string-match "[ \t\n]+" target i)) + (setq target (concat (substring target 0 i) " " + (substring target (match-end 0)))) + (setq i (+ i 1))) + (Info-goto-node target))) + +(defun Info-extract-menu-node-name (&optional errmessage multi-line) + (skip-chars-forward " \t\n") + (let ((beg (point)) + str i) + (skip-chars-forward "^:") + (forward-char 1) + (setq str + (if (looking-at ":") + (buffer-substring beg (1- (point))) + (skip-chars-forward " \t\n") + (Info-following-node-name (if multi-line "^.,\t" "^.,\t\n")))) + (while (setq i (string-match "\n" str i)) + (aset str i ?\ )) + ;; Collapse multiple spaces. + (while (string-match " +" str) + (setq str (replace-match " " t t str))) + str)) + +;; No one calls this. +;;(defun Info-menu-item-sequence (list) +;; (while list +;; (Info-menu (car list)) +;; (setq list (cdr list)))) + +(defun Info-complete-menu-item (string predicate action) + (let ((case-fold-search t)) + (cond ((eq action nil) + (let (completions + (pattern (concat "\n\\* \\(" + (regexp-quote string) + "[^:\t\n]*\\):"))) + (save-excursion + (set-buffer Info-complete-menu-buffer) + (goto-char (point-min)) + (while (re-search-forward pattern nil t) + (setq completions (cons (cons (format "%s" + (buffer-substring + (match-beginning 1) + (match-end 1))) + (match-beginning 1)) + completions)))) + (try-completion string completions predicate))) + ((eq action t) + (let (completions + (pattern (concat "\n\\* \\(" + (regexp-quote string) + "[^:\t\n]*\\):"))) + (save-excursion + (set-buffer Info-complete-menu-buffer) + (goto-char (point-min)) + (while (re-search-forward pattern nil t) + (setq completions (cons (cons (format "%s" + (buffer-substring + (match-beginning 1) + (match-end 1))) + (match-beginning 1)) + completions)))) + (all-completions string completions predicate))) + (t + (save-excursion + (set-buffer Info-complete-menu-buffer) + (goto-char (point-min)) + (re-search-forward (concat "\n\\* " + (regexp-quote string) + ":") + nil t)))))) + + +(defun Info-menu (menu-item) + "Go to node for menu item named (or abbreviated) NAME. +Completion is allowed, and the menu item point is on is the default." + (interactive + (let ((completions '()) + ;; If point is within a menu item, use that item as the default + (default nil) + (p (point)) + beg + (last nil)) + (save-excursion + (goto-char (point-min)) + (if (not (search-forward "\n* menu:" nil t)) + (error "No menu in this node")) + (setq beg (point)) + (and (< (point) p) + (save-excursion + (goto-char p) + (end-of-line) + (re-search-backward "\n\\* \\([^:\t\n]*\\):" beg t) + (setq default (format "%s" (buffer-substring + (match-beginning 1) + (match-end 1))))))) + (let ((item nil)) + (while (null item) + (setq item (let ((completion-ignore-case t) + (Info-complete-menu-buffer (current-buffer))) + (completing-read (if default + (format "Menu item (default %s): " + default) + "Menu item: ") + 'Info-complete-menu-item nil t))) + ;; we rely on the fact that completing-read accepts an input + ;; of "" even when the require-match argument is true and "" + ;; is not a valid possibility + (if (string= item "") + (if default + (setq item default) + ;; ask again + (setq item nil)))) + (list item)))) + ;; there is a problem here in that if several menu items have the same + ;; name you can only go to the node of the first with this command. + (Info-goto-node (Info-extract-menu-item menu-item))) + +(defun Info-extract-menu-item (menu-item) + (setq menu-item (regexp-quote menu-item)) + (save-excursion + (goto-char (point-min)) + (or (search-forward "\n* menu:" nil t) + (error "No menu in this node")) + (or (re-search-forward (concat "\n\\* " menu-item ":") nil t) + (re-search-forward (concat "\n\\* " menu-item) nil t) + (error "No such item in menu")) + (beginning-of-line) + (forward-char 2) + (Info-extract-menu-node-name))) + +;; If COUNT is nil, use the last item in the menu. +(defun Info-extract-menu-counting (count) + (save-excursion + (goto-char (point-min)) + (or (search-forward "\n* menu:" nil t) + (error "No menu in this node")) + (if count + (or (search-forward "\n* " nil t count) + (error "Too few items in menu")) + (while (search-forward "\n* " nil t) + nil)) + (Info-extract-menu-node-name))) + +(defun Info-nth-menu-item () + "Go to the node of the Nth menu item. +N is the digit argument used to invoke this command." + (interactive) + (Info-goto-node + (Info-extract-menu-counting + (- (aref (this-command-keys) (1- (length (this-command-keys)))) ?0)))) + +(defun Info-top-node () + "Go to the Top node of this file." + (interactive) + (Info-goto-node "Top")) + +(defun Info-final-node () + "Go to the final node in this file." + (interactive) + (Info-goto-node "Top") + (let (Info-history) + ;; Go to the last node in the menu of Top. + (Info-goto-node (Info-extract-menu-counting nil)) + ;; If the last node in the menu is not last in pointer structure, + ;; move forward until we can't go any farther. + (while (Info-forward-node t t) nil) + ;; Then keep moving down to last subnode, unless we reach an index. + (while (and (not (string-match "\\<index\\>" Info-current-node)) + (save-excursion (search-forward "\n* Menu:" nil t))) + (Info-goto-node (Info-extract-menu-counting nil))))) + +(defun Info-forward-node (&optional not-down no-error) + "Go forward one node, considering all nodes as forming one sequence." + (interactive) + (goto-char (point-min)) + (forward-line 1) + ;; three possibilities, in order of priority: + ;; 1. next node is in a menu in this node (but not in an index) + ;; 2. next node is next at same level + ;; 3. next node is up and next + (cond ((and (not not-down) + (save-excursion (search-forward "\n* menu:" nil t)) + (not (string-match "\\<index\\>" Info-current-node))) + (Info-goto-node (Info-extract-menu-counting 1)) + t) + ((save-excursion (search-backward "next:" nil t)) + (Info-next) + t) + ((and (save-excursion (search-backward "up:" nil t)) + ;; Use string-equal, not equal, to ignore text props. + (not (string-equal (downcase (Info-extract-pointer "up")) + "top"))) + (let ((old-node Info-current-node)) + (Info-up) + (let (Info-history success) + (unwind-protect + (setq success (Info-forward-node t no-error)) + (or success (Info-goto-node old-node)))))) + (no-error nil) + (t (error "No pointer forward from this node")))) + +(defun Info-backward-node () + "Go backward one node, considering all nodes as forming one sequence." + (interactive) + (let ((prevnode (Info-extract-pointer "prev[ious]*" t)) + (upnode (Info-extract-pointer "up" t))) + (cond ((and upnode (string-match "(" upnode)) + (error "First node in file")) + ((and upnode (or (null prevnode) + ;; Use string-equal, not equal, + ;; to ignore text properties. + (string-equal (downcase prevnode) + (downcase upnode)))) + (Info-up)) + (prevnode + ;; If we move back at the same level, + ;; go down to find the last subnode*. + (Info-prev) + (let (Info-history) + (while (and (not (string-match "\\<index\\>" Info-current-node)) + (save-excursion (search-forward "\n* Menu:" nil t))) + (Info-goto-node (Info-extract-menu-counting nil))))) + (t + (error "No pointer backward from this node"))))) + +(defun Info-exit () + "Exit Info by selecting some other buffer." + (interactive) + (if Info-standalone + (save-buffers-kill-emacs) + (switch-to-buffer (prog1 (other-buffer (current-buffer)) + (bury-buffer (current-buffer)))))) + +(defun Info-next-menu-item () + (interactive) + (save-excursion + (forward-line -1) + (search-forward "\n* menu:" nil t) + (or (search-forward "\n* " nil t) + (error "No more items in menu")) + (Info-goto-node (Info-extract-menu-node-name)))) + +(defun Info-last-menu-item () + (interactive) + (save-excursion + (forward-line 1) + (let ((beg (save-excursion + (and (search-backward "\n* menu:" nil t) + (point))))) + (or (and beg (search-backward "\n* " beg t)) + (error "No previous items in menu"))) + (Info-goto-node (save-excursion + (goto-char (match-end 0)) + (Info-extract-menu-node-name))))) + +(defmacro Info-no-error (&rest body) + (list 'condition-case nil (cons 'progn (append body '(t))) '(error nil))) + +(defun Info-next-preorder () + "Go to the next subnode or the next node, or go up a level." + (interactive) + (cond ((Info-no-error (Info-next-menu-item))) + ((Info-no-error (Info-next))) + ((Info-no-error (Info-up)) + ;; Since we have already gone thru all the items in this menu, + ;; go up to the end of this node. + (goto-char (point-max))) + (t + (error "No more nodes")))) + +(defun Info-last-preorder () + "Go to the last node, popping up a level if there is none." + (interactive) + (cond ((Info-no-error + (Info-last-menu-item) + ;; If we go down a menu item, go to the end of the node + ;; so we can scroll back through it. + (goto-char (point-max))) + (recenter -1)) + ((Info-no-error (Info-prev)) + (goto-char (point-max)) + (recenter -1)) + ((Info-no-error (Info-up)) + (goto-char (point-min)) + (or (search-forward "\n* Menu:" nil t) + (goto-char (point-max)))) + (t (error "No previous nodes")))) + +(defun Info-scroll-up () + "Scroll one screenful forward in Info, considering all nodes as one sequence. +Once you scroll far enough in a node that its menu appears on the screen, +the next scroll moves into its first subnode. When you scroll past +the end of a node, that goes to the next node or back up to the parent node." + (interactive) + (if (or (< (window-start) (point-min)) + (> (window-start) (point-max))) + (set-window-start (selected-window) (point))) + (let ((virtual-end (save-excursion + (goto-char (point-min)) + (if (search-forward "\n* Menu:" nil t) + (point) + (point-max))))) + (if (or (< virtual-end (window-start)) + (pos-visible-in-window-p virtual-end)) + (Info-next-preorder) + (scroll-up)))) + +(defun Info-scroll-down () + "Scroll one screenful back in Info, considering all nodes as one sequence. +Within the menu of a node, this goes to its last subnode. +When you scroll past the beginning of a node, that goes to the +previous node or back up to the parent node." + (interactive) + (if (or (< (window-start) (point-min)) + (> (window-start) (point-max))) + (set-window-start (selected-window) (point))) + (let ((virtual-end (save-excursion + (goto-char (point-min)) + (search-forward "\n* Menu:" nil t)))) + (if (or virtual-end (pos-visible-in-window-p (point-min))) + (Info-last-preorder) + (scroll-down)))) + +(defun Info-next-reference () + "Move cursor to the next cross-reference or menu item in the node." + (interactive) + (let ((pat "\\*note[ \n\t]*\\([^:]*\\):\\|^\\* .*:") + (old-pt (point))) + (or (eobp) (forward-char 1)) + (or (re-search-forward pat nil t) + (progn + (goto-char (point-min)) + (or (re-search-forward pat nil t) + (progn + (goto-char old-pt) + (error "No cross references in this node"))))) + (goto-char (match-beginning 0)) + (if (looking-at "\\* Menu:") + (Info-next-reference)))) + +(defun Info-prev-reference () + "Move cursor to the previous cross-reference or menu item in the node." + (interactive) + (let ((pat "\\*note[ \n\t]*\\([^:]*\\):\\|^\\* .*:") + (old-pt (point))) + (or (re-search-backward pat nil t) + (progn + (goto-char (point-max)) + (or (re-search-backward pat nil t) + (progn + (goto-char old-pt) + (error "No cross references in this node"))))) + (goto-char (match-beginning 0)) + (if (looking-at "\\* Menu:") + (Info-prev-reference)))) + +(defun Info-index (topic) + "Look up a string in the index for this file. +The index is defined as the first node in the top-level menu whose +name contains the word \"Index\", plus any immediately following +nodes whose names also contain the word \"Index\". +If there are no exact matches to the specified topic, this chooses +the first match which is a case-insensitive substring of a topic. +Use the `,' command to see the other matches. +Give a blank topic name to go to the Index node itself." + (interactive "sIndex topic: ") + (let ((orignode Info-current-node) + (rnode nil) + (pattern (format "\n\\* \\([^\n:]*%s[^\n:]*\\):[ \t]*\\([^.\n]*\\)\\.[ \t]*\\([0-9]*\\)" + (regexp-quote topic))) + node) + (Info-goto-node "Top") + (or (search-forward "\n* menu:" nil t) + (error "No index")) + (or (re-search-forward "\n\\* \\(.*\\<Index\\>\\)" nil t) + (error "No index")) + (goto-char (match-beginning 1)) + ;; Here, and subsequently in this function, + ;; we bind Info-history to nil for internal node-switches + ;; so that we don't put junk in the history. + ;; In the first Info-goto-node call, above, we do update the history + ;; because that is what the user's previous node choice into it. + (let ((Info-history nil)) + (Info-goto-node (Info-extract-menu-node-name))) + (or (equal topic "") + (let ((matches nil) + (exact nil) + (Info-history nil) + found) + (while + (progn + (goto-char (point-min)) + (while (re-search-forward pattern nil t) + (setq matches + (cons (list (buffer-substring (match-beginning 1) + (match-end 1)) + (buffer-substring (match-beginning 2) + (match-end 2)) + Info-current-node + (string-to-int (concat "0" + (buffer-substring + (match-beginning 3) + (match-end 3))))) + matches))) + (and (setq node (Info-extract-pointer "next" t)) + (string-match "\\<Index\\>" node))) + (Info-goto-node node)) + (or matches + (progn + (Info-last) + (error "No \"%s\" in index" topic))) + ;; Here it is a feature that assoc is case-sensitive. + (while (setq found (assoc topic matches)) + (setq exact (cons found exact) + matches (delq found matches))) + (setq Info-index-alternatives (nconc exact (nreverse matches))) + (Info-index-next 0))))) + +(defun Info-index-next (num) + "Go to the next matching index item from the last `i' command." + (interactive "p") + (or Info-index-alternatives + (error "No previous `i' command in this file")) + (while (< num 0) + (setq num (+ num (length Info-index-alternatives)))) + (while (> num 0) + (setq Info-index-alternatives + (nconc (cdr Info-index-alternatives) + (list (car Info-index-alternatives))) + num (1- num))) + (Info-goto-node (nth 1 (car Info-index-alternatives))) + (if (> (nth 3 (car Info-index-alternatives)) 0) + (forward-line (nth 3 (car Info-index-alternatives))) + (forward-line 3) ; don't search in headers + (let ((name (car (car Info-index-alternatives)))) + (if (or (re-search-forward (format + "\\(Function\\|Command\\): %s\\( \\|$\\)" + (regexp-quote name)) nil t) + (search-forward (format "`%s'" name) nil t) + (and (string-match "\\`.*\\( (.*)\\)\\'" name) + (search-forward + (format "`%s'" (substring name 0 (match-beginning 1))) + nil t)) + (search-forward name nil t)) + (beginning-of-line) + (goto-char (point-min))))) + (message "Found \"%s\" in %s. %s" + (car (car Info-index-alternatives)) + (nth 2 (car Info-index-alternatives)) + (if (cdr Info-index-alternatives) + "(Press `,' for more)" + "(Only match)"))) + +(defun Info-undefined () + "Make command be undefined in Info." + (interactive) + (ding)) + +(defun Info-help () + "Enter the Info tutorial." + (interactive) + (delete-other-windows) + (Info-find-node "info" + (if (< (window-height) 23) + "Help-Small-Screen" + "Help"))) + +(defun Info-summary () + "Display a brief summary of all Info commands." + (interactive) + (save-window-excursion + (switch-to-buffer "*Help*") + (erase-buffer) + (insert (documentation 'Info-mode)) + (help-mode) + (goto-char (point-min)) + (let (ch flag) + (while (progn (setq flag (not (pos-visible-in-window-p (point-max)))) + (message (if flag "Type Space to see more" + "Type Space to return to Info")) + (if (not (eq ?\ (setq ch (read-event)))) + (progn (setq unread-command-events (list ch)) nil) + flag)) + (scroll-up))) + (bury-buffer "*Help*"))) + +(defun Info-get-token (pos start all &optional errorstring) + "Return the token around POS, +POS must be somewhere inside the token +START is a regular expression which will match the + beginning of the tokens delimited string +ALL is a regular expression with a single + parenthized subpattern which is the token to be + returned. E.g. '{\(.*\)}' would return any string + enclosed in braces around POS. +SIG optional fourth argument, controls action on no match + nil: return nil + t: beep + a string: signal an error, using that string." + (save-excursion + (goto-char pos) + (re-search-backward start (max (point-min) (- pos 200)) 'yes) + (let (found) + (while (and (re-search-forward all (min (point-max) (+ pos 200)) 'yes) + (not (setq found (and (<= (match-beginning 0) pos) + (> (match-end 0) pos)))))) + (if (and found (<= (match-beginning 0) pos) + (> (match-end 0) pos)) + (buffer-substring (match-beginning 1) (match-end 1)) + (cond ((null errorstring) + nil) + ((eq errorstring t) + (beep) + nil) + (t + (error "No %s around position %d" errorstring pos))))))) + +(defun Info-mouse-follow-nearest-node (click) + "\\<Info-mode-map>Follow a node reference near point. +Like \\[Info-menu], \\[Info-follow-reference], \\[Info-next], \\[Info-prev] or \\[Info-up] command, depending on where you click. +At end of the node's text, moves to the next node, or up if none." + (interactive "e") + (let* ((start (event-start click)) + (window (car start)) + (pos (car (cdr start)))) + (select-window window) + (goto-char pos)) + (and (not (Info-try-follow-nearest-node)) + (save-excursion (forward-line 1) (eobp)) + (Info-next-preorder))) + +(defun Info-follow-nearest-node () + "\\<Info-mode-map>Follow a node reference near point. +Like \\[Info-menu], \\[Info-follow-reference], \\[Info-next], \\[Info-prev] or \\[Info-up] command, depending on where point is. +If no reference to follow, moves to the next node, or up if none." + (interactive) + (or (Info-try-follow-nearest-node) + (Info-next-preorder))) + +;; Common subroutine. +(defun Info-try-follow-nearest-node () + "Follow a node reference near point. Return non-nil if successful." + (let (node) + (cond + ((setq node (Info-get-token (point) "\\*note[ \n]" + "\\*note[ \n]\\([^:]*\\):")) + (Info-follow-reference node)) + ((setq node (Info-get-token (point) "\\* " "\\* \\([^:]*\\)::")) + (Info-goto-node node)) + ((setq node (Info-get-token (point) "\\* " "\\* \\([^:]*\\):")) + (Info-menu node)) + ((setq node (Info-get-token (point) "Up: " "Up: \\([^,\n\t]*\\)")) + (Info-goto-node node)) + ((setq node (Info-get-token (point) "Next: " "Next: \\([^,\n\t]*\\)")) + (Info-goto-node node)) + ((setq node (Info-get-token (point) "File: " "File: \\([^,\n\t]*\\)")) + (Info-goto-node "Top")) + ((setq node (Info-get-token (point) "Prev: " "Prev: \\([^,\n\t]*\\)")) + (Info-goto-node node))) + node)) + +(defvar Info-mode-map nil + "Keymap containing Info commands.") +(if Info-mode-map + nil + (setq Info-mode-map (make-keymap)) + (suppress-keymap Info-mode-map) + (define-key Info-mode-map "." 'beginning-of-buffer) + (define-key Info-mode-map " " 'Info-scroll-up) + (define-key Info-mode-map "\C-m" 'Info-follow-nearest-node) + (define-key Info-mode-map "\t" 'Info-next-reference) + (define-key Info-mode-map "\e\t" 'Info-prev-reference) + (define-key Info-mode-map "1" 'Info-nth-menu-item) + (define-key Info-mode-map "2" 'Info-nth-menu-item) + (define-key Info-mode-map "3" 'Info-nth-menu-item) + (define-key Info-mode-map "4" 'Info-nth-menu-item) + (define-key Info-mode-map "5" 'Info-nth-menu-item) + (define-key Info-mode-map "6" 'Info-nth-menu-item) + (define-key Info-mode-map "7" 'Info-nth-menu-item) + (define-key Info-mode-map "8" 'Info-nth-menu-item) + (define-key Info-mode-map "9" 'Info-nth-menu-item) + (define-key Info-mode-map "0" 'undefined) + (define-key Info-mode-map "?" 'Info-summary) + (define-key Info-mode-map "]" 'Info-forward-node) + (define-key Info-mode-map "[" 'Info-backward-node) + (define-key Info-mode-map "<" 'Info-top-node) + (define-key Info-mode-map ">" 'Info-final-node) + (define-key Info-mode-map "b" 'beginning-of-buffer) + (define-key Info-mode-map "d" 'Info-directory) + (define-key Info-mode-map "e" 'Info-edit) + (define-key Info-mode-map "f" 'Info-follow-reference) + (define-key Info-mode-map "g" 'Info-goto-node) + (define-key Info-mode-map "h" 'Info-help) + (define-key Info-mode-map "i" 'Info-index) + (define-key Info-mode-map "l" 'Info-last) + (define-key Info-mode-map "m" 'Info-menu) + (define-key Info-mode-map "n" 'Info-next) + (define-key Info-mode-map "p" 'Info-prev) + (define-key Info-mode-map "q" 'Info-exit) + (define-key Info-mode-map "s" 'Info-search) + ;; For consistency with Rmail. + (define-key Info-mode-map "\M-s" 'Info-search) + (define-key Info-mode-map "t" 'Info-top-node) + (define-key Info-mode-map "u" 'Info-up) + (define-key Info-mode-map "," 'Info-index-next) + (define-key Info-mode-map "\177" 'Info-scroll-down) + (define-key Info-mode-map [mouse-2] 'Info-mouse-follow-nearest-node) + ) + +;; Info mode is suitable only for specially formatted data. +(put 'info-mode 'mode-class 'special) + +(defun Info-mode () + "\\<Info-mode-map> +Info mode provides commands for browsing through the Info documentation tree. +Documentation in Info is divided into \"nodes\", each of which discusses +one topic and contains references to other nodes which discuss related +topics. Info has commands to follow the references and show you other nodes. + +\\[Info-help] Invoke the Info tutorial. + +Selecting other nodes: +\\[Info-mouse-follow-nearest-node] + Follow a node reference you click on. + This works with menu items, cross references, and + the \"next\", \"previous\" and \"up\", depending on where you click. +\\[Info-next] Move to the \"next\" node of this node. +\\[Info-prev] Move to the \"previous\" node of this node. +\\[Info-up] Move \"up\" from this node. +\\[Info-menu] Pick menu item specified by name (or abbreviation). + Picking a menu item causes another node to be selected. +\\[Info-directory] Go to the Info directory node. +\\[Info-follow-reference] Follow a cross reference. Reads name of reference. +\\[Info-last] Move to the last node you were at. +\\[Info-index] Look up a topic in this file's Index and move to that node. +\\[Info-index-next] (comma) Move to the next match from a previous `i' command. + +Moving within a node: +\\[Info-scroll-up] Normally, scroll forward a full screen. If the end of the buffer is +already visible, try to go to the next menu entry, or up if there is none. +\\[Info-scroll-down] Normally, scroll backward. If the beginning of the buffer is +already visible, try to go to the previous menu entry, or up if there is none. +\\[beginning-of-buffer] Go to beginning of node. + +Advanced commands: +\\[Info-exit] Quit Info: reselect previously selected buffer. +\\[Info-edit] Edit contents of selected node. +1 Pick first item in node's menu. +2, 3, 4, 5 Pick second ... fifth item in node's menu. +\\[Info-goto-node] Move to node specified by name. + You may include a filename as well, as (FILENAME)NODENAME. +\\[universal-argument] \\[info] Move to new Info file with completion. +\\[Info-search] Search through this Info file for specified regexp, + and select the node in which the next occurrence is found. +\\[Info-next-reference] Move cursor to next cross-reference or menu item. +\\[Info-prev-reference] Move cursor to previous cross-reference or menu item." + (kill-all-local-variables) + (setq major-mode 'Info-mode) + (setq mode-name "Info") + (use-local-map Info-mode-map) + (set-syntax-table text-mode-syntax-table) + (setq local-abbrev-table text-mode-abbrev-table) + (setq case-fold-search t) + (setq buffer-read-only t) + (make-local-variable 'Info-current-file) + (make-local-variable 'Info-current-subfile) + (make-local-variable 'Info-current-node) + (make-local-variable 'Info-tag-table-marker) + (make-local-variable 'Info-history) + (make-local-variable 'Info-index-alternatives) + (if (memq (framep (selected-frame)) '(x pc)) + (progn + (make-face 'info-node) + (make-face 'info-menu-5) + (make-face 'info-xref) + (or (face-differs-from-default-p 'info-node) + (if (face-differs-from-default-p 'bold-italic) + (copy-face 'bold-italic 'info-node) + (copy-face 'bold 'info-node))) + (or (face-differs-from-default-p 'info-menu-5) + (set-face-underline-p 'info-menu-5 t)) + (or (face-differs-from-default-p 'info-xref) + (copy-face 'bold 'info-xref))) + (setq Info-fontify nil)) + (Info-set-mode-line) + (run-hooks 'Info-mode-hook)) + +(defvar Info-edit-map nil + "Local keymap used within `e' command of Info.") +(if Info-edit-map + nil + (setq Info-edit-map (nconc (make-sparse-keymap) text-mode-map)) + (define-key Info-edit-map "\C-c\C-c" 'Info-cease-edit)) + +;; Info-edit mode is suitable only for specially formatted data. +(put 'info-edit-mode 'mode-class 'special) + +(defun Info-edit-mode () + "Major mode for editing the contents of an Info node. +Like text mode with the addition of `Info-cease-edit' +which returns to Info mode for browsing. +\\{Info-edit-map}" + (use-local-map Info-edit-map) + (setq major-mode 'Info-edit-mode) + (setq mode-name "Info Edit") + (kill-local-variable 'mode-line-buffer-identification) + (setq buffer-read-only nil) + (force-mode-line-update) + (buffer-enable-undo (current-buffer)) + (run-hooks 'Info-edit-mode-hook)) + +(defun Info-edit () + "Edit the contents of this Info node. +Allowed only if variable `Info-enable-edit' is non-nil." + (interactive) + (or Info-enable-edit + (error "Editing info nodes is not enabled")) + (Info-edit-mode) + (message (substitute-command-keys + "Editing: Type \\<Info-edit-map>\\[Info-cease-edit] to return to info"))) + +(defun Info-cease-edit () + "Finish editing Info node; switch back to Info proper." + (interactive) + ;; Do this first, so nothing has changed if user C-g's at query. + (and (buffer-modified-p) + (y-or-n-p "Save the file? ") + (save-buffer)) + (use-local-map Info-mode-map) + (setq major-mode 'Info-mode) + (setq mode-name "Info") + (Info-set-mode-line) + (setq buffer-read-only t) + (force-mode-line-update) + (and (marker-position Info-tag-table-marker) + (buffer-modified-p) + (message "Tags may have changed. Use Info-tagify if necessary"))) + +(defun Info-find-emacs-command-nodes (command) + "Return a list of locations documenting COMMAND in the Emacs Info manual. +The locations are of the format used in Info-history, i.e. +\(FILENAME NODENAME BUFFERPOS\)." + (require 'info) + (let ((where '()) + (cmd-desc (concat "^\\* " (regexp-quote (symbol-name command)) + ":\\s *\\(.*\\)\\.$"))) + (save-excursion + (Info-find-node "emacs" "Command Index") + ;; Take the index node off the Info history. + (setq Info-history (cdr Info-history)) + (goto-char (point-max)) + (while (re-search-backward cmd-desc nil t) + (setq where (cons (list Info-current-file + (buffer-substring + (match-beginning 1) + (match-end 1)) + 0) + where))) + where))) + +;;;###autoload +(defun Info-goto-emacs-command-node (command) + "Go to the Info node in the Emacs manual for command COMMAND. +The command is found by looking up in Emacs manual's Command Index." + (interactive "CFind documentation for command: ") + (or (commandp command) + (signal 'wrong-type-argument (list 'commandp command))) + (let ((where (Info-find-emacs-command-nodes command))) + (if where + (let ((num-matches (length where))) + ;; Get Info running, and pop to it in another window. + (save-window-excursion + (info)) + (pop-to-buffer "*info*") + (Info-find-node (car (car where)) + (car (cdr (car where)))) + (if (> num-matches 1) + (progn + ;; Info-find-node already pushed (car where) onto + ;; Info-history. Put the other nodes that were found on + ;; the history. + (setq Info-history (nconc (cdr where) Info-history)) + (message (substitute-command-keys + "Found %d other entr%s. Use \\[Info-last] to see %s.") + (1- num-matches) + (if (> num-matches 2) "ies" "y") + (if (> num-matches 2) "them" "it"))))) + (error "Couldn't find documentation for %s." command)))) + +;;;###autoload +(defun Info-goto-emacs-key-command-node (key) + "Go to the Info node in the Emacs manual the command bound to KEY, a string. +Interactively, if the binding is execute-extended-command, a command is read. +The command is found by looking up in Emacs manual's Command Index." + (interactive "kFind documentation for key:") + (let ((command (key-binding key))) + (cond ((null command) + (message "%s is undefined" (key-description key))) + ((and (interactive-p) + (eq command 'execute-extended-command)) + (Info-goto-emacs-command-node + (read-command "Find documentation for command: "))) + (t + (Info-goto-emacs-command-node command))))) + +(defun Info-fontify-node () + (save-excursion + (let ((buffer-read-only nil)) + (goto-char (point-min)) + (if (looking-at "^File: [^,: \t]+,?[ \t]+") + (progn + (goto-char (match-end 0)) + (while + (looking-at "[ \t]*[^:, \t\n]+:[ \t]+\\([^:,\t\n]+\\),?") + (goto-char (match-end 0)) + (put-text-property (match-beginning 1) (match-end 1) + 'face 'info-xref) + (put-text-property (match-beginning 1) (match-end 1) + 'mouse-face 'highlight)))) + (goto-char (point-min)) + (while (re-search-forward "\\*Note[ \n\t]+\\([^:]*\\):" nil t) + (if (= (char-after (1- (match-beginning 0))) ?\") ; hack + nil + (put-text-property (match-beginning 1) (match-end 1) + 'face 'info-xref) + (put-text-property (match-beginning 1) (match-end 1) + 'mouse-face 'highlight))) + (goto-char (point-min)) + (if (and (search-forward "\n* Menu:" nil t) + (not (string-match "\\<Index\\>" Info-current-node)) + ;; Don't take time to annotate huge menus + (< (- (point-max) (point)) Info-fontify-maximum-menu-size)) + (let ((n 0)) + (while (re-search-forward "^\\* \\([^:\t\n]*\\):" nil t) + (setq n (1+ n)) + (if (memq n '(5 9)) ; visual aids to help with 1-9 keys + (put-text-property (match-beginning 0) + (1+ (match-beginning 0)) + 'face 'info-menu-5)) + (put-text-property (match-beginning 1) (match-end 1) + 'face 'info-node) + (put-text-property (match-beginning 1) (match-end 1) + 'mouse-face 'highlight)))) + (set-buffer-modified-p nil)))) + +(provide 'info) + +;;; info.el ends here diff --git a/gnu/usr.bin/texinfo/emacs/informat.el b/gnu/usr.bin/texinfo/emacs/informat.el new file mode 100644 index 00000000000..2d923a1570d --- /dev/null +++ b/gnu/usr.bin/texinfo/emacs/informat.el @@ -0,0 +1,425 @@ +;;; informat.el --- info support functions package for Emacs + +;; Copyright (C) 1986 Free Software Foundation, Inc. + +;; Maintainer: FSF +;; Keywords: help + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +;;; Code: + +(require 'info) + +;;;###autoload +(defun Info-tagify () + "Create or update Info-file tag table in current buffer." + (interactive) + ;; Save and restore point and restrictions. + ;; save-restrictions would not work + ;; because it records the old max relative to the end. + ;; We record it relative to the beginning. + (message "Tagifying %s ..." (file-name-nondirectory (buffer-file-name))) + (let ((omin (point-min)) + (omax (point-max)) + (nomax (= (point-max) (1+ (buffer-size)))) + (opoint (point))) + (unwind-protect + (progn + (widen) + (goto-char (point-min)) + (if (search-forward "\^_\nIndirect:\n" nil t) + (message "Cannot tagify split info file") + (let ((regexp "Node:[ \t]*\\([^,\n\t]*\\)[,\t\n]") + (case-fold-search t) + list) + (while (search-forward "\n\^_" nil t) + ;; We want the 0-origin character position of the ^_. + ;; That is the same as the Emacs (1-origin) position + ;; of the newline before it. + (let ((beg (match-beginning 0))) + (forward-line 2) + (if (re-search-backward regexp beg t) + (setq list + (cons (list (buffer-substring + (match-beginning 1) + (match-end 1)) + beg) + list))))) + (goto-char (point-max)) + (forward-line -8) + (let ((buffer-read-only nil)) + (if (search-forward "\^_\nEnd tag table\n" nil t) + (let ((end (point))) + (search-backward "\nTag table:\n") + (beginning-of-line) + (delete-region (point) end))) + (goto-char (point-max)) + (insert "\^_\f\nTag table:\n") + (move-marker Info-tag-table-marker (point)) + (setq list (nreverse list)) + (while list + (insert "Node: " (car (car list)) ?\177) + (princ (car (cdr (car list))) (current-buffer)) + (insert ?\n) + (setq list (cdr list))) + (insert "\^_\nEnd tag table\n"))))) + (goto-char opoint) + (narrow-to-region omin (if nomax (1+ (buffer-size)) + (min omax (point-max)))))) + (message "Tagifying %s ... done" (file-name-nondirectory (buffer-file-name)))) + +;;;###autoload +(defun Info-split () + "Split an info file into an indirect file plus bounded-size subfiles. +Each subfile will be up to 50,000 characters plus one node. + +To use this command, first visit a large Info file that has a tag +table. The buffer is modified into a (small) indirect info file which +should be saved in place of the original visited file. + +The subfiles are written in the same directory the original file is +in, with names generated by appending `-' and a number to the original +file name. The indirect file still functions as an Info file, but it +contains just the tag table and a directory of subfiles." + + (interactive) + (if (< (buffer-size) 70000) + (error "This is too small to be worth splitting")) + (goto-char (point-min)) + (search-forward "\^_") + (forward-char -1) + (let ((start (point)) + (chars-deleted 0) + subfiles + (subfile-number 1) + (case-fold-search t) + (filename (file-name-sans-versions buffer-file-name))) + (goto-char (point-max)) + (forward-line -8) + (setq buffer-read-only nil) + (or (search-forward "\^_\nEnd tag table\n" nil t) + (error "Tag table required; use M-x Info-tagify")) + (search-backward "\nTag table:\n") + (if (looking-at "\nTag table:\n\^_") + (error "Tag table is just a skeleton; use M-x Info-tagify")) + (beginning-of-line) + (forward-char 1) + (save-restriction + (narrow-to-region (point-min) (point)) + (goto-char (point-min)) + (while (< (1+ (point)) (point-max)) + (goto-char (min (+ (point) 50000) (point-max))) + (search-forward "\^_" nil 'move) + (setq subfiles + (cons (list (+ start chars-deleted) + (concat (file-name-nondirectory filename) + (format "-%d" subfile-number))) + subfiles)) + ;; Put a newline at end of split file, to make Unix happier. + (insert "\n") + (write-region (point-min) (point) + (concat filename (format "-%d" subfile-number))) + (delete-region (1- (point)) (point)) + ;; Back up over the final ^_. + (forward-char -1) + (setq chars-deleted (+ chars-deleted (- (point) start))) + (delete-region start (point)) + (setq subfile-number (1+ subfile-number)))) + (while subfiles + (goto-char start) + (insert (nth 1 (car subfiles)) + (format ": %d" (1- (car (car subfiles)))) + "\n") + (setq subfiles (cdr subfiles))) + (goto-char start) + (insert "\^_\nIndirect:\n") + (search-forward "\nTag Table:\n") + (insert "(Indirect)\n"))) + +;;;###autoload +(defun Info-validate () + "Check current buffer for validity as an Info file. +Check that every node pointer points to an existing node." + (interactive) + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (if (search-forward "\nTag table:\n(Indirect)\n" nil t) + (error "Don't yet know how to validate indirect info files: \"%s\"" + (buffer-name (current-buffer)))) + (goto-char (point-min)) + (let ((allnodes '(("*"))) + (regexp "Node:[ \t]*\\([^,\n\t]*\\)[,\t\n]") + (case-fold-search t) + (tags-losing nil) + (lossages ())) + (while (search-forward "\n\^_" nil t) + (forward-line 1) + (let ((beg (point))) + (forward-line 1) + (if (re-search-backward regexp beg t) + (let ((name (downcase + (buffer-substring + (match-beginning 1) + (progn + (goto-char (match-end 1)) + (skip-chars-backward " \t") + (point)))))) + (if (assoc name allnodes) + (setq lossages + (cons (list name "Duplicate node-name" nil) + lossages)) + (setq allnodes + (cons (list name + (progn + (end-of-line) + (and (re-search-backward + "prev[ious]*:" beg t) + (progn + (goto-char (match-end 0)) + (downcase + (Info-following-node-name))))) + beg) + allnodes))))))) + (goto-char (point-min)) + (while (search-forward "\n\^_" nil t) + (forward-line 1) + (let ((beg (point)) + thisnode next) + (forward-line 1) + (if (re-search-backward regexp beg t) + (save-restriction + (search-forward "\n\^_" nil 'move) + (narrow-to-region beg (point)) + (setq thisnode (downcase + (buffer-substring + (match-beginning 1) + (progn + (goto-char (match-end 1)) + (skip-chars-backward " \t") + (point))))) + (end-of-line) + (and (search-backward "next:" nil t) + (setq next (Info-validate-node-name "invalid Next")) + (assoc next allnodes) + (if (equal (car (cdr (assoc next allnodes))) + thisnode) + ;; allow multiple `next' pointers to one node + (let ((tem lossages)) + (while tem + (if (and (equal (car (cdr (car tem))) + "should have Previous") + (equal (car (car tem)) + next)) + (setq lossages (delq (car tem) lossages))) + (setq tem (cdr tem)))) + (setq lossages + (cons (list next + "should have Previous" + thisnode) + lossages)))) + (end-of-line) + (if (re-search-backward "prev[ious]*:" nil t) + (Info-validate-node-name "invalid Previous")) + (end-of-line) + (if (search-backward "up:" nil t) + (Info-validate-node-name "invalid Up")) + (if (re-search-forward "\n* Menu:" nil t) + (while (re-search-forward "\n\\* " nil t) + (Info-validate-node-name + (concat "invalid menu item " + (buffer-substring (point) + (save-excursion + (skip-chars-forward "^:") + (point)))) + (Info-extract-menu-node-name)))) + (goto-char (point-min)) + (while (re-search-forward "\\*note[ \n]*[^:\t]*:" nil t) + (goto-char (+ (match-beginning 0) 5)) + (skip-chars-forward " \n") + (Info-validate-node-name + (concat "invalid reference " + (buffer-substring (point) + (save-excursion + (skip-chars-forward "^:") + (point)))) + (Info-extract-menu-node-name "Bad format cross-reference"))))))) + (setq tags-losing (not (Info-validate-tags-table))) + (if (or lossages tags-losing) + (with-output-to-temp-buffer " *problems in info file*" + (while lossages + (princ "In node \"") + (princ (car (car lossages))) + (princ "\", ") + (let ((tem (nth 1 (car lossages)))) + (cond ((string-match "\n" tem) + (princ (substring tem 0 (match-beginning 0))) + (princ "...")) + (t + (princ tem)))) + (if (nth 2 (car lossages)) + (progn + (princ ": ") + (let ((tem (nth 2 (car lossages)))) + (cond ((string-match "\n" tem) + (princ (substring tem 0 (match-beginning 0))) + (princ "...")) + (t + (princ tem)))))) + (terpri) + (setq lossages (cdr lossages))) + (if tags-losing (princ "\nTags table must be recomputed\n"))) + ;; Here if info file is valid. + ;; If we already made a list of problems, clear it out. + (save-excursion + (if (get-buffer " *problems in info file*") + (progn + (set-buffer " *problems in info file*") + (kill-buffer (current-buffer))))) + (message "File appears valid")))))) + +(defun Info-validate-node-name (kind &optional name) + (if name + nil + (goto-char (match-end 0)) + (skip-chars-forward " \t") + (if (= (following-char) ?\() + nil + (setq name + (buffer-substring + (point) + (progn + (skip-chars-forward "^,\t\n") + (skip-chars-backward " ") + (point)))))) + (if (null name) + nil + (setq name (downcase name)) + (or (and (> (length name) 0) (= (aref name 0) ?\()) + (assoc name allnodes) + (setq lossages + (cons (list thisnode kind name) lossages)))) + name) + +(defun Info-validate-tags-table () + (goto-char (point-min)) + (if (not (search-forward "\^_\nEnd tag table\n" nil t)) + t + (not (catch 'losing + (let* ((end (match-beginning 0)) + (start (progn (search-backward "\nTag table:\n") + (1- (match-end 0)))) + tem) + (setq tem allnodes) + (while tem + (goto-char start) + (or (equal (car (car tem)) "*") + (search-forward (concat "Node: " + (car (car tem)) + "\177") + end t) + (throw 'losing 'x)) + (setq tem (cdr tem))) + (goto-char (1+ start)) + (while (looking-at ".*Node: \\(.*\\)\177\\([0-9]+\\)$") + (setq tem (downcase (buffer-substring + (match-beginning 1) + (match-end 1)))) + (setq tem (assoc tem allnodes)) + (if (or (not tem) + (< 1000 (progn + (goto-char (match-beginning 2)) + (setq tem (- (car (cdr (cdr tem))) + (read (current-buffer)))) + (if (> tem 0) tem (- tem))))) + (throw 'losing 'y))) + (forward-line 1)) + (or (looking-at "End tag table\n") + (throw 'losing 'z)) + nil)))) + +;;;###autoload +(defun batch-info-validate () + "Runs `Info-validate' on the files remaining on the command line. +Must be used only with -batch, and kills Emacs on completion. +Each file will be processed even if an error occurred previously. +For example, invoke \"emacs -batch -f batch-info-validate $info/ ~/*.info\"" + (if (not noninteractive) + (error "batch-info-validate may only be used -batch.")) + (let ((version-control t) + (auto-save-default nil) + (find-file-run-dired nil) + (kept-old-versions 259259) + (kept-new-versions 259259)) + (let ((error 0) + file + (files ())) + (while command-line-args-left + (setq file (expand-file-name (car command-line-args-left))) + (cond ((not (file-exists-p file)) + (message ">> %s does not exist!" file) + (setq error 1 + command-line-args-left (cdr command-line-args-left))) + ((file-directory-p file) + (setq command-line-args-left (nconc (directory-files file) + (cdr command-line-args-left)))) + (t + (setq files (cons file files) + command-line-args-left (cdr command-line-args-left))))) + (while files + (setq file (car files) + files (cdr files)) + (let ((lose nil)) + (condition-case err + (progn + (if buffer-file-name (kill-buffer (current-buffer))) + (find-file file) + (buffer-disable-undo (current-buffer)) + (set-buffer-modified-p nil) + (fundamental-mode) + (let ((case-fold-search nil)) + (goto-char (point-max)) + (cond ((search-backward "\n\^_\^L\nTag table:\n" nil t) + (message "%s already tagified" file)) + ((< (point-max) 30000) + (message "%s too small to bother tagifying" file)) + (t + (Info-tagify)))) + (let ((loss-name " *problems in info file*")) + (message "Checking validity of info file %s..." file) + (if (get-buffer loss-name) + (kill-buffer loss-name)) + (Info-validate) + (if (not (get-buffer loss-name)) + nil ;(message "Checking validity of info file %s... OK" file) + (message "----------------------------------------------------------------------") + (message ">> PROBLEMS IN INFO FILE %s" file) + (save-excursion + (set-buffer loss-name) + (princ (buffer-substring (point-min) (point-max)))) + (message "----------------------------------------------------------------------") + (setq error 1 lose t))) + (if (and (buffer-modified-p) + (not lose)) + (progn (message "Saving modified %s" file) + (save-buffer)))) + (error (message ">> Error: %s" (prin1-to-string err)))))) + (kill-emacs error)))) + +;;; informat.el ends here diff --git a/gnu/usr.bin/texinfo/emacs/makeinfo.el b/gnu/usr.bin/texinfo/emacs/makeinfo.el new file mode 100644 index 00000000000..61269de9e2c --- /dev/null +++ b/gnu/usr.bin/texinfo/emacs/makeinfo.el @@ -0,0 +1,245 @@ +;;;; makeinfo.el -- run makeinfo conveniently. +;;; Copyright (C) 1991, 1993 Free Software Foundation, Inc. + +;;; Author: Robert J. Chassell +;;; Maintainer: FSF + +;;; This file is part of GNU Emacs. + +;; GNU Emacs is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +;;; Commentary: + +;;; The Texinfo mode `makeinfo' related commands are: + +;; makeinfo-region to run makeinfo on the current region. +;; makeinfo-buffer to run makeinfo on the current buffer, or +;; with optional prefix arg, on current region +;; kill-compilation to kill currently running makeinfo job +;; makeinfo-recenter-makeinfo-buffer to redisplay *compilation* buffer + +;;; Keybindings (defined in `texinfo.el') + +;; makeinfo bindings +; (define-key texinfo-mode-map "\C-c\C-m\C-r" 'makeinfo-region) +; (define-key texinfo-mode-map "\C-c\C-m\C-b" 'makeinfo-buffer) +; (define-key texinfo-mode-map "\C-c\C-m\C-k" 'kill-compilation) +; (define-key texinfo-mode-map "\C-c\C-m\C-l" +; 'makeinfo-recenter-compilation-buffer) + +;;; Code: + +;;; Variables used by `makeinfo' + +(require 'compile) + +(defvar makeinfo-run-command "makeinfo" + "*Command used to run `makeinfo' subjob. +The name of the file is appended to this string, separated by a space.") + +(defvar makeinfo-options "--fill-column=70" + "*String containing options for running `makeinfo'. +Do not include `--footnote-style' or `--paragraph-indent'; +the proper way to specify those is with the Texinfo commands +`@footnotestyle` and `@paragraphindent'.") + +(require 'texinfo) + +(defvar makeinfo-compilation-process nil + "Process that runs `makeinfo'. Should start out nil.") + +(defvar makeinfo-temp-file nil + "Temporary file name used for text being sent as input to `makeinfo'.") + +(defvar makeinfo-output-file-name nil + "Info file name used for text output by `makeinfo'.") + + +;;; The `makeinfo' function definitions + +(defun makeinfo-region (region-beginning region-end) + "Make Info file from region of current Texinfo file, and switch to it. + +This command does not offer the `next-error' feature since it would +apply to a temporary file, not the original; use the `makeinfo-buffer' +command to gain use of `next-error'." + + (interactive "r") + (let (filename-or-header + filename-or-header-beginning + filename-or-header-end) + ;; Cannot use `let' for makeinfo-temp-file or + ;; makeinfo-output-file-name since `makeinfo-compilation-sentinel' + ;; needs them. + + (setq makeinfo-temp-file + (concat + (make-temp-name + (substring (buffer-file-name) + 0 + (or (string-match "\\.tex" (buffer-file-name)) + (length (buffer-file-name))))) + ".texinfo")) + + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (let ((search-end (save-excursion (forward-line 100) (point)))) + ;; Find and record the Info filename, + ;; or else explain that a filename is needed. + (if (re-search-forward + "^@setfilename[ \t]+\\([^ \t\n]+\\)[ \t]*" + search-end t) + (setq makeinfo-output-file-name + (buffer-substring (match-beginning 1) (match-end 1))) + (error + "The texinfo file needs a line saying: @setfilename <name>")) + + ;; Find header and specify its beginning and end. + (goto-char (point-min)) + (if (and + (prog1 + (search-forward tex-start-of-header search-end t) + (beginning-of-line) + ;; Mark beginning of header. + (setq filename-or-header-beginning (point))) + (prog1 + (search-forward tex-end-of-header nil t) + (beginning-of-line) + ;; Mark end of header + (setq filename-or-header-end (point)))) + + ;; Insert the header into the temporary file. + (write-region + (min filename-or-header-beginning region-beginning) + filename-or-header-end + makeinfo-temp-file nil nil) + + ;; Else no header; insert @filename line into temporary file. + (goto-char (point-min)) + (search-forward "@setfilename" search-end t) + (beginning-of-line) + (setq filename-or-header-beginning (point)) + (forward-line 1) + (setq filename-or-header-end (point)) + (write-region + (min filename-or-header-beginning region-beginning) + filename-or-header-end + makeinfo-temp-file nil nil)) + + ;; Insert the region into the file. + (write-region + (max region-beginning filename-or-header-end) + region-end + makeinfo-temp-file t nil) + + ;; Run the `makeinfo-compile' command in the *compilation* buffer + (save-excursion + (makeinfo-compile + (concat makeinfo-run-command + " " + makeinfo-options + " " + makeinfo-temp-file) + "Use `makeinfo-buffer' to gain use of the `next-error' command" + nil))))))) + +;;; Actually run makeinfo. COMMAND is the command to run. +;;; ERROR-MESSAGE is what to say when next-error can't find another error. +;;; If PARSE-ERRORS is non-nil, do try to parse error messages. +(defun makeinfo-compile (command error-message parse-errors) + (let ((buffer + (compile-internal command error-message nil + (and (not parse-errors) + ;; If we do want to parse errors, pass nil. + ;; Otherwise, use this function, which won't + ;; ever find any errors. + '(lambda (&rest ignore) + (setq compilation-error-list nil)))))) + (set-process-sentinel (get-buffer-process buffer) + 'makeinfo-compilation-sentinel))) + +;; Delete makeinfo-temp-file after processing is finished, +;; and visit Info file. +;; This function is called when the compilation process changes state. +;; Based on `compilation-sentinel' in compile.el +(defun makeinfo-compilation-sentinel (proc msg) + (compilation-sentinel proc msg) + (if (and makeinfo-temp-file (file-exists-p makeinfo-temp-file)) + (delete-file makeinfo-temp-file)) + ;; Always use the version on disk. + (if (get-file-buffer makeinfo-output-file-name) + (progn (set-buffer makeinfo-output-file-name) + (revert-buffer t t)) + (find-file makeinfo-output-file-name)) + (goto-char (point-min))) + +(defun makeinfo-buffer () + "Make Info file from current buffer. + +Use the \\[next-error] command to move to the next error +\(if there are errors\)." + + (interactive) + (cond ((null buffer-file-name) + (error "Buffer not visiting any file")) + ((buffer-modified-p) + (if (y-or-n-p "Buffer modified; do you want to save it? ") + (save-buffer)))) + + ;; Find and record the Info filename, + ;; or else explain that a filename is needed. + (save-excursion + (goto-char (point-min)) + (let ((search-end (save-excursion (forward-line 100) (point)))) + (if (re-search-forward + "^@setfilename[ \t]+\\([^ \t\n]+\\)[ \t]*" + search-end t) + (setq makeinfo-output-file-name + (buffer-substring (match-beginning 1) (match-end 1))) + (error + "The texinfo file needs a line saying: @setfilename <name>")))) + + (save-excursion + (makeinfo-compile + (concat makeinfo-run-command " " makeinfo-options + " " buffer-file-name) + "No more errors." + t))) + +(defun makeinfo-recenter-compilation-buffer (linenum) + "Redisplay `*compilation*' buffer so most recent output can be seen. +The last line of the buffer is displayed on +line LINE of the window, or centered if LINE is nil." + (interactive "P") + (let ((makeinfo-buffer (get-buffer "*compilation*")) + (old-buffer (current-buffer))) + (if (null makeinfo-buffer) + (message "No *compilation* buffer") + (pop-to-buffer makeinfo-buffer) + (bury-buffer makeinfo-buffer) + (goto-char (point-max)) + (recenter (if linenum + (prefix-numeric-value linenum) + (/ (window-height) 2))) + (pop-to-buffer old-buffer) + ))) + +;;; Place `provide' at end of file. +(provide 'makeinfo) + +;;; makeinfo.el ends here + diff --git a/gnu/usr.bin/texinfo/emacs/texinfmt.el b/gnu/usr.bin/texinfo/emacs/texinfmt.el new file mode 100644 index 00000000000..d24e3d7c683 --- /dev/null +++ b/gnu/usr.bin/texinfo/emacs/texinfmt.el @@ -0,0 +1,3067 @@ +;;;; texinfmt.el +;;; Copyright (C) 1985, 1986, 1988, 1990, 1991, +;;; 1992, 1993, 1994, 1995 Free Software Foundation, Inc. + +;; Maintainer: Robert J. Chassell <bug-texinfo@prep.ai.mit.edu> + +;;; This file is part of GNU Emacs. + +;; GNU Emacs is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +;;; Code: + +;;; Emacs lisp functions to convert Texinfo files to Info files. + +(defvar texinfmt-version "2.34 of 7 June 1995") + +;;; Variable definitions + +(require 'texinfo) ; So `texinfo-footnote-style' is defined. +(require 'texnfo-upd) ; So `texinfo-section-types-regexp' is defined. + +(defvar texinfo-format-syntax-table nil) + +(defvar texinfo-vindex) +(defvar texinfo-findex) +(defvar texinfo-cindex) +(defvar texinfo-pindex) +(defvar texinfo-tindex) +(defvar texinfo-kindex) +(defvar texinfo-last-node) +(defvar texinfo-node-names) +(defvar texinfo-enclosure-list) +(defvar texinfo-alias-list) + +(defvar texinfo-command-start) +(defvar texinfo-command-end) +(defvar texinfo-command-name) +(defvar texinfo-defun-type) +(defvar texinfo-last-node-pos) +(defvar texinfo-stack) +(defvar texinfo-short-index-cmds-alist) +(defvar texinfo-short-index-format-cmds-alist) +(defvar texinfo-format-filename) +(defvar texinfo-footnote-number) +(defvar texinfo-start-of-header) +(defvar texinfo-end-of-header) +(defvar texinfo-raisesections-alist) +(defvar texinfo-lowersections-alist) + +;;; Syntax table + +(if texinfo-format-syntax-table + nil + (setq texinfo-format-syntax-table (make-syntax-table)) + (modify-syntax-entry ?\" " " texinfo-format-syntax-table) + (modify-syntax-entry ?\\ " " texinfo-format-syntax-table) + (modify-syntax-entry ?@ "\\" texinfo-format-syntax-table) + (modify-syntax-entry ?\^q "\\" texinfo-format-syntax-table) + (modify-syntax-entry ?\[ "." texinfo-format-syntax-table) + (modify-syntax-entry ?\] "." texinfo-format-syntax-table) + (modify-syntax-entry ?\( "." texinfo-format-syntax-table) + (modify-syntax-entry ?\) "." texinfo-format-syntax-table) + (modify-syntax-entry ?{ "(}" texinfo-format-syntax-table) + (modify-syntax-entry ?} "){" texinfo-format-syntax-table) + (modify-syntax-entry ?\' "." texinfo-format-syntax-table)) + + +;;; Top level buffer and region formatting functions + +;;;###autoload +(defun texinfo-format-buffer (&optional notagify) + "Process the current buffer as texinfo code, into an Info file. +The Info file output is generated in a buffer visiting the Info file +names specified in the @setfilename command. + +Non-nil argument (prefix, if interactive) means don't make tag table +and don't split the file if large. You can use Info-tagify and +Info-split to do these manually." + (interactive "P") + (let ((lastmessage "Formatting Info file...")) + (message lastmessage) + (texinfo-format-buffer-1) + (if notagify + nil + (if (> (buffer-size) 30000) + (progn + (message (setq lastmessage "Making tags table for Info file...")) + (Info-tagify))) + (if (> (buffer-size) 100000) + (progn + (message (setq lastmessage "Splitting Info file...")) + (Info-split)))) + (message (concat lastmessage + (if (interactive-p) "done. Now save it." "done."))))) + +(defvar texinfo-region-buffer-name "*Info Region*" + "*Name of the temporary buffer used by \\[texinfo-format-region].") + +;;;###autoload +(defun texinfo-format-region (region-beginning region-end) + "Convert the current region of the Texinfo file to Info format. +This lets you see what that part of the file will look like in Info. +The command is bound to \\[texinfo-format-region]. The text that is +converted to Info is stored in a temporary buffer." + (interactive "r") + (message "Converting region to Info format...") + (let (texinfo-command-start + texinfo-command-end + texinfo-command-name + texinfo-vindex + texinfo-findex + texinfo-cindex + texinfo-pindex + texinfo-tindex + texinfo-kindex + texinfo-stack + (texinfo-format-filename "") + texinfo-example-start + texinfo-last-node-pos + texinfo-last-node + texinfo-node-names + (texinfo-footnote-number 0) + last-input-buffer + (fill-column-for-info fill-column) + (input-buffer (current-buffer)) + (input-directory default-directory) + (header-text "") + (header-beginning 1) + (header-end 1)) + +;;; Copy lines between beginning and end of header lines, +;;; if any, or else copy the `@setfilename' line, if any. + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (let ((search-end (save-excursion (forward-line 100) (point)))) + (if (or + ;; Either copy header text. + (and + (prog1 + (search-forward tex-start-of-header search-end t) + (forward-line 1) + ;; Mark beginning of header. + (setq header-beginning (point))) + (prog1 + (search-forward tex-end-of-header nil t) + (beginning-of-line) + ;; Mark end of header + (setq header-end (point)))) + ;; Or copy @filename line. + (prog2 + (goto-char (point-min)) + (search-forward "@setfilename" search-end t) + (beginning-of-line) + (setq header-beginning (point)) + (forward-line 1) + (setq header-end (point)))) + + ;; Copy header + (setq header-text + (buffer-substring + (min header-beginning region-beginning) + header-end)))))) + +;;; Find a buffer to use. + (switch-to-buffer (get-buffer-create texinfo-region-buffer-name)) + (erase-buffer) + ;; Insert the header into the buffer. + (insert header-text) + ;; Insert the region into the buffer. + (insert-buffer-substring + input-buffer + (max region-beginning header-end) + region-end) + ;; Make sure region ends in a newline. + (or (= (preceding-char) ?\n) + (insert "\n")) + + (goto-char (point-min)) + (texinfo-mode) + (message "Converting region to Info format...") + (setq fill-column fill-column-for-info) + ;; Install a syntax table useful for scanning command operands. + (set-syntax-table texinfo-format-syntax-table) + + ;; Insert @include files so `texinfo-raise-lower-sections' can + ;; work on them without losing track of multiple + ;; @raise/@lowersections commands. + (while (re-search-forward "^@include" nil t) + (setq texinfo-command-end (point)) + (let ((filename (concat input-directory + (texinfo-parse-line-arg)))) + (re-search-backward "^@include") + (delete-region (point) (save-excursion (forward-line 1) (point))) + (message "Reading included file: %s" filename) + (save-excursion + (save-restriction + (narrow-to-region + (point) + (+ (point) (car (cdr (insert-file-contents filename))))) + (goto-char (point-min)) + ;; Remove `@setfilename' line from included file, if any, + ;; so @setfilename command not duplicated. + (if (re-search-forward + "^@setfilename" (save-excursion (forward-line 100) (point)) t) + (progn + (beginning-of-line) + (delete-region + (point) (save-excursion (forward-line 1) (point))))))))) + + ;; Raise or lower level of each section, if necessary. + (goto-char (point-min)) + (texinfo-raise-lower-sections) + ;; Append @refill to appropriate paragraphs for filling. + (goto-char (point-min)) + (texinfo-append-refill) + ;; If the region includes the effective end of the data, + ;; discard everything after that. + (goto-char (point-max)) + (if (re-search-backward "^@bye" nil t) + (delete-region (point) (point-max))) + ;; Make sure buffer ends in a newline. + (or (= (preceding-char) ?\n) + (insert "\n")) + ;; Don't use a previous value of texinfo-enclosure-list. + (setq texinfo-enclosure-list nil) + (setq texinfo-alias-list nil) + + (goto-char (point-min)) + (if (looking-at "\\\\input[ \t]+texinfo") + (delete-region (point) (save-excursion (forward-line 1) (point)))) + + ;; Insert Info region title text. + (goto-char (point-min)) + (if (search-forward + "@setfilename" (save-excursion (forward-line 100) (point)) t) + (progn + (setq texinfo-command-end (point)) + (beginning-of-line) + (setq texinfo-command-start (point)) + (let ((arg (texinfo-parse-arg-discard))) + (insert " " + texinfo-region-buffer-name + " buffer for: `") + (insert (file-name-nondirectory (expand-file-name arg))) + (insert "', -*-Text-*-\n"))) + ;; Else no `@setfilename' line + (insert " " + texinfo-region-buffer-name + " buffer -*-Text-*-\n")) + (insert "produced by `texinfo-format-region'\n" + "from a region in: " + (if (buffer-file-name input-buffer) + (concat "`" + (file-name-sans-versions + (file-name-nondirectory + (buffer-file-name input-buffer))) + "'") + (concat "buffer `" (buffer-name input-buffer) "'")) + "\nusing `texinfmt.el' version " + texinfmt-version + ".\n\n") + + ;; Now convert for real. + (goto-char (point-min)) + (texinfo-format-scan) + (goto-char (point-min)) + + (message "Done."))) + + +;;; Primary internal formatting function for the whole buffer. + +(defun texinfo-format-buffer-1 () + (let (texinfo-format-filename + texinfo-example-start + texinfo-command-start + texinfo-command-end + texinfo-command-name + texinfo-last-node + texinfo-last-node-pos + texinfo-vindex + texinfo-findex + texinfo-cindex + texinfo-pindex + texinfo-tindex + texinfo-kindex + texinfo-stack + texinfo-node-names + (texinfo-footnote-number 0) + last-input-buffer + outfile + (fill-column-for-info fill-column) + (input-buffer (current-buffer)) + (input-directory default-directory)) + (setq texinfo-enclosure-list nil) + (setq texinfo-alias-list nil) + (save-excursion + (goto-char (point-min)) + (or (search-forward "@setfilename" nil t) + (error "Texinfo file needs an `@setfilename FILENAME' line.")) + (setq texinfo-command-end (point)) + (setq outfile (texinfo-parse-line-arg))) + (find-file outfile) + (texinfo-mode) + (setq fill-column fill-column-for-info) + (set-syntax-table texinfo-format-syntax-table) + (erase-buffer) + (insert-buffer-substring input-buffer) + (message "Converting %s to Info format..." (buffer-name input-buffer)) + + ;; Insert @include files so `texinfo-raise-lower-sections' can + ;; work on them without losing track of multiple + ;; @raise/@lowersections commands. + (goto-char (point-min)) + (while (re-search-forward "^@include" nil t) + (setq texinfo-command-end (point)) + (let ((filename (concat input-directory + (texinfo-parse-line-arg)))) + (re-search-backward "^@include") + (delete-region (point) (save-excursion (forward-line 1) (point))) + (message "Reading included file: %s" filename) + (save-excursion + (save-restriction + (narrow-to-region + (point) + (+ (point) (car (cdr (insert-file-contents filename))))) + (goto-char (point-min)) + ;; Remove `@setfilename' line from included file, if any, + ;; so @setfilename command not duplicated. + (if (re-search-forward + "^@setfilename" + (save-excursion (forward-line 100) (point)) t) + (progn + (beginning-of-line) + (delete-region + (point) (save-excursion (forward-line 1) (point))))))))) + ;; Raise or lower level of each section, if necessary. + (goto-char (point-min)) + (texinfo-raise-lower-sections) + ;; Append @refill to appropriate paragraphs + (goto-char (point-min)) + (texinfo-append-refill) + (goto-char (point-min)) + (search-forward "@setfilename") + (beginning-of-line) + (delete-region (point-min) (point)) + ;; Remove @bye at end of file, if it is there. + (goto-char (point-max)) + (if (search-backward "@bye" nil t) + (delete-region (point) (point-max))) + ;; Make sure buffer ends in a newline. + (or (= (preceding-char) ?\n) + (insert "\n")) + ;; Scan the whole buffer, converting to Info format. + (texinfo-format-scan) + ;; Return data for indices. + (goto-char (point-min)) + (list outfile + texinfo-vindex texinfo-findex texinfo-cindex + texinfo-pindex texinfo-tindex texinfo-kindex))) + + +;;; Perform non-@-command file conversions: quotes and hyphens + +(defun texinfo-format-convert (min max) + ;; Convert left and right quotes to typewriter font quotes. + (goto-char min) + (while (search-forward "``" max t) + (replace-match "\"")) + (goto-char min) + (while (search-forward "''" max t) + (replace-match "\"")) + ;; Convert three hyphens in a row to two. + (goto-char min) + (while (re-search-forward "\\( \\|\\w\\)\\(---\\)\\( \\|\\w\\)" max t) + (delete-region (1+ (match-beginning 2)) (+ 2 (match-beginning + 2))))) + + +;;; Handle paragraph filling + +(defvar texinfo-no-refill-regexp + "^@\\(example\\|smallexample\\|lisp\\|smalllisp\\|display\\|format\\|flushleft\\|flushright\\|menu\\|titlepage\\|iftex\\|ifhtml\\|tex\\|html\\)" + "Regexp specifying environments in which paragraphs are not filled.") + +(defvar texinfo-part-of-para-regexp + "^@\\(b{\\|bullet{\\|cite{\\|code{\\|emph{\\|equiv{\\|error{\\|expansion{\\|file{\\|i{\\|inforef{\\|kbd{\\|key{\\|lisp{\\|minus{\\|point{\\|print{\\|pxref{\\|r{\\|ref{\\|result{\\|samp{\\|sc{\\|t{\\|TeX{\\|today{\\|var{\\|w{\\|xref{\\)" + "Regexp specifying @-commands found within paragraphs.") + +(defun texinfo-append-refill () + "Append @refill at end of each paragraph that should be filled. +Do not append @refill to paragraphs within @example and similar environments. +Do not append @refill to paragraphs containing @w{TEXT} or @*." + + ;; It is necessary to append @refill before other processing because + ;; the other processing removes information that tells Texinfo + ;; whether the text should or should not be filled. + + (while (< (point) (point-max)) + (let ((refill-blank-lines "^[ \t\n]*$") + (case-fold-search nil)) ; Don't confuse @TeX and @tex.... + (beginning-of-line) + ;; 1. Skip over blank lines; + ;; skip over lines beginning with @-commands, + ;; but do not skip over lines + ;; that are no-refill environments such as @example or + ;; that begin with within-paragraph @-commands such as @code. + (while (and (looking-at (concat "^@\\|^\\\\\\|" refill-blank-lines)) + (not (looking-at + (concat + "\\(" + texinfo-no-refill-regexp + "\\|" + texinfo-part-of-para-regexp + "\\)"))) + (< (point) (point-max))) + (forward-line 1)) + ;; 2. Skip over @example and similar no-refill environments. + (if (looking-at texinfo-no-refill-regexp) + (let ((environment + (buffer-substring (match-beginning 1) (match-end 1)))) + (progn (re-search-forward (concat "^@end " environment) nil t) + (forward-line 1))) + ;; 3. Do not refill a paragraph containing @w or @* + (if (or + (>= (point) (point-max)) + (re-search-forward + "@w{\\|@\\*" (save-excursion (forward-paragraph) (point)) t)) + ;; Go to end of paragraph and do nothing. + (forward-paragraph) + ;; 4. Else go to end of paragraph and insert @refill + (forward-paragraph) + (forward-line -1) + (end-of-line) + (delete-region + (point) + (save-excursion (skip-chars-backward " \t") (point))) + ;; `looking-at-backward' not available in v. 18.57 + ;; (if (not (looking-at-backward "@refill\\|@bye")) ;) + (if (not (re-search-backward + "@refill\\|@bye" + (save-excursion (beginning-of-line) (point)) + t)) + (insert "@refill")) + (forward-line 1)))))) + + +;;; Handle `@raisesections' and `@lowersections' commands + +;; These commands change the hierarchical level of chapter structuring +;; commands. +;; +;; @raisesections changes @subsection to @section, +;; @section to @chapter, +;; etc. +;; +;; @lowersections changes @chapter to @section +;; @subsection to @subsubsection, +;; etc. +;; +;; An @raisesections/@lowersections command changes only those +;; structuring commands that follow the @raisesections/@lowersections +;; command. +;; +;; Repeated @raisesections/@lowersections continue to raise or lower +;; the heading level. +;; +;; An @lowersections command cancels an @raisesections command, and +;; vice versa. +;; +;; You cannot raise or lower "beyond" chapters or subsubsections, but +;; trying to do so does not elicit an error---you just get more +;; headings that mean the same thing as you keep raising or lowering +;; (for example, after a single @raisesections, both @chapter and +;; @section produce chapter headings). + +(defun texinfo-raise-lower-sections () + "Raise or lower the hierarchical level of chapters, sections, etc. + +This function acts according to `@raisesections' and `@lowersections' +commands in the Texinfo file. + +For example, an `@lowersections' command is useful if you wish to +include what is written as an outer or standalone Texinfo file in +another Texinfo file as an inner, included file. The `@lowersections' +command changes chapters to sections, sections to subsections and so +on. + +@raisesections changes @subsection to @section, + @section to @chapter, + @heading to @chapheading, + etc. + +@lowersections changes @chapter to @section, + @subsection to @subsubsection, + @heading to @subheading, + etc. + +An `@raisesections' or `@lowersections' command changes only those +structuring commands that follow the `@raisesections' or +`@lowersections' command. + +An `@lowersections' command cancels an `@raisesections' command, and +vice versa. + +Repeated use of the commands continue to raise or lower the hierarchical +level a step at a time. + +An attempt to raise above `chapters' reproduces chapter commands; an +attempt to lower below subsubsections reproduces subsubsection +commands." + + ;; `texinfo-section-types-regexp' is defined in `texnfo-upd.el'; + ;; it is a regexp matching chapter, section, other headings + ;; (but not the top node). + + (let (type (level 0)) + (while + (re-search-forward + (concat + "\\(\\(^@\\(raise\\|lower\\)sections\\)\\|\\(" + texinfo-section-types-regexp + "\\)\\)") + nil t) + (beginning-of-line) + (save-excursion (setq type (read (current-buffer)))) + (cond + + ;; 1. Increment level + ((eq type '@raisesections) + (setq level (1+ level)) + (delete-region + (point) (save-excursion (forward-line 1) (point)))) + + ;; 2. Decrement level + ((eq type '@lowersections) + (setq level (1- level)) + (delete-region + (point) (save-excursion (forward-line 1) (point)))) + + ;; Now handle structuring commands + ((cond + + ;; 3. Raise level when positive + ((> level 0) + (let ((count level) + (new-level type)) + (while (> count 0) + (setq new-level + (cdr (assq new-level texinfo-raisesections-alist))) + (setq count (1- count))) + (kill-word 1) + (insert (symbol-name new-level)))) + + ;; 4. Do nothing except move point when level is zero + ((= level 0) (forward-line 1)) + + ;; 5. Lower level when positive + ((< level 0) + (let ((count level) + (new-level type)) + (while (< count 0) + (setq new-level + (cdr (assq new-level texinfo-lowersections-alist))) + (setq count (1+ count))) + (kill-word 1) + (insert (symbol-name new-level)))))))))) + +(defvar texinfo-raisesections-alist + '((@chapter . @chapter) ; Cannot go higher + (@unnumbered . @unnumbered) + + (@majorheading . @majorheading) + (@chapheading . @chapheading) + (@appendix . @appendix) + + (@section . @chapter) + (@unnumberedsec . @unnumbered) + (@heading . @chapheading) + (@appendixsec . @appendix) + + (@subsection . @section) + (@unnumberedsubsec . @unnumberedsec) + (@subheading . @heading) + (@appendixsubsec . @appendixsec) + + (@subsubsection . @subsection) + (@unnumberedsubsubsec . @unnumberedsubsec) + (@subsubheading . @subheading) + (@appendixsubsubsec . @appendixsubsec)) + "*An alist of next higher levels for chapters, sections. etc. +For example, section to chapter, subsection to section. +Used by `texinfo-raise-lower-sections'. +The keys specify types of section; the values correspond to the next +higher types.") + +(defvar texinfo-lowersections-alist + '((@chapter . @section) + (@unnumbered . @unnumberedsec) + (@majorheading . @heading) + (@chapheading . @heading) + (@appendix . @appendixsec) + + (@section . @subsection) + (@unnumberedsec . @unnumberedsubsec) + (@heading . @subheading) + (@appendixsec . @appendixsubsec) + + (@subsection . @subsubsection) + (@unnumberedsubsec . @unnumberedsubsubsec) + (@subheading . @subsubheading) + (@appendixsubsec . @appendixsubsubsec) + + (@subsubsection . @subsubsection) ; Cannot go lower. + (@unnumberedsubsubsec . @unnumberedsubsubsec) + (@subsubheading . @subsubheading) + (@appendixsubsubsec . @appendixsubsubsec)) + "*An alist of next lower levels for chapters, sections. etc. +For example, chapter to section, section to subsection. +Used by `texinfo-raise-lower-sections'. +The keys specify types of section; the values correspond to the next +lower types.") + + +;;; Perform those texinfo-to-info conversions that apply to the whole input +;;; uniformly. + +(defun texinfo-format-scan () + (texinfo-format-convert (point-min) (point-max)) + ;; Scan for @-commands. + (goto-char (point-min)) + (while (search-forward "@" nil t) + (if (looking-at "[@{}^'` *\"?!]") + ;; Handle a few special @-followed-by-one-char commands. + (if (= (following-char) ?*) + (progn + ;; remove command + (delete-region (1- (point)) (1+ (point))) + ;; insert return if not at end of line; + ;; else line is already broken. + (if (not (= (following-char) ?\n)) + (insert ?\n))) + ;; The other characters are simply quoted. Delete the @. + (delete-char -1) + (forward-char 1)) + ;; @ is followed by a command-word; find the end of the word. + (setq texinfo-command-start (1- (point))) + (if (= (char-syntax (following-char)) ?w) + (forward-word 1) + (forward-char 1)) + (setq texinfo-command-end (point)) + ;; Handle let aliasing + (setq texinfo-command-name + (let (trial + (cmdname + (buffer-substring + (1+ texinfo-command-start) texinfo-command-end))) + (while (setq trial (assoc cmdname texinfo-alias-list)) + (setq cmdname (cdr trial))) + (intern cmdname))) + ;; Call the handler for this command. + (let ((enclosure-type + (assoc + (symbol-name texinfo-command-name) + texinfo-enclosure-list))) + (if enclosure-type + (progn + (insert + (car (car (cdr enclosure-type))) + (texinfo-parse-arg-discard) + (car (cdr (car (cdr enclosure-type))))) + (goto-char texinfo-command-start)) + (let ((cmd (get texinfo-command-name 'texinfo-format))) + (if cmd (funcall cmd) (texinfo-unsupported))))))) + + (cond (texinfo-stack + (goto-char (nth 2 (car texinfo-stack))) + (error "Unterminated @%s" (car (car texinfo-stack)))))) + +(put 'begin 'texinfo-format 'texinfo-format-begin) +(defun texinfo-format-begin () + (texinfo-format-begin-end 'texinfo-format)) + +(put 'end 'texinfo-format 'texinfo-format-end) +(defun texinfo-format-end () + (texinfo-format-begin-end 'texinfo-end)) + +(defun texinfo-format-begin-end (prop) + (setq texinfo-command-name (intern (texinfo-parse-line-arg))) + (let ((cmd (get texinfo-command-name prop))) + (if cmd (funcall cmd) + (texinfo-unsupported)))) + +;;; Parsing functions + +(defun texinfo-parse-line-arg () + (goto-char texinfo-command-end) + (let ((start (point))) + (cond ((looking-at " ") + (skip-chars-forward " ") + (setq start (point)) + (end-of-line) + (skip-chars-backward " ") + (delete-region (point) (progn (end-of-line) (point))) + (setq texinfo-command-end (1+ (point)))) + ((looking-at "{") + (setq start (1+ (point))) + (forward-list 1) + (setq texinfo-command-end (point)) + (forward-char -1)) + (t + (error "Invalid texinfo command arg format"))) + (prog1 (buffer-substring start (point)) + (if (eolp) (forward-char 1))))) + +(defun texinfo-parse-expanded-arg () + (goto-char texinfo-command-end) + (let ((start (point)) + marker) + (cond ((looking-at " ") + (skip-chars-forward " ") + (setq start (point)) + (end-of-line) + (setq texinfo-command-end (1+ (point)))) + ((looking-at "{") + (setq start (1+ (point))) + (forward-list 1) + (setq texinfo-command-end (point)) + (forward-char -1)) + (t + (error "Invalid texinfo command arg format"))) + (setq marker (move-marker (make-marker) texinfo-command-end)) + (texinfo-format-expand-region start (point)) + (setq texinfo-command-end (marker-position marker)) + (move-marker marker nil) + (prog1 (buffer-substring start (point)) + (if (eolp) (forward-char 1))))) + +(defun texinfo-format-expand-region (start end) + (save-restriction + (narrow-to-region start end) + (let (texinfo-command-start + texinfo-command-end + texinfo-command-name + texinfo-stack) + (texinfo-format-scan)) + (goto-char (point-max)))) + +(defun texinfo-parse-arg-discard () + (prog1 (texinfo-parse-line-arg) + (texinfo-discard-command))) + +(defun texinfo-discard-command () + (delete-region texinfo-command-start texinfo-command-end)) + +(defun texinfo-optional-braces-discard () + "Discard braces following command, if any." + (goto-char texinfo-command-end) + (let ((start (point))) + (cond ((looking-at "[ \t]*\n")) ; do nothing + ((looking-at "{") ; remove braces, if any + (forward-list 1) + (setq texinfo-command-end (point))) + (t + (error + "Invalid `texinfo-optional-braces-discard' format \(need braces?\)"))) + (delete-region texinfo-command-start texinfo-command-end))) + +(defun texinfo-format-parse-line-args () + (let ((start (1- (point))) + next beg end + args) + (skip-chars-forward " ") + (while (not (eolp)) + (setq beg (point)) + (re-search-forward "[\n,]") + (setq next (point)) + (if (bolp) (setq next (1- next))) + (forward-char -1) + (skip-chars-backward " ") + (setq end (point)) + (setq args (cons (if (> end beg) (buffer-substring beg end)) + args)) + (goto-char next) + (skip-chars-forward " ")) + (if (eolp) (forward-char 1)) + (setq texinfo-command-end (point)) + (nreverse args))) + +(defun texinfo-format-parse-args () + (let ((start (1- (point))) + next beg end + args) + (search-forward "{") + (save-excursion + (texinfo-format-expand-region + (point) + (save-excursion (up-list 1) (1- (point))))) + ;; The following does not handle cross references of the form: + ;; `@xref{bullet, , @code{@@bullet}@{@}}.' because the + ;; re-search-forward finds the first right brace after the second + ;; comma. + (while (/= (preceding-char) ?\}) + (skip-chars-forward " \t\n") + (setq beg (point)) + (re-search-forward "[},]") + (setq next (point)) + (forward-char -1) + (skip-chars-backward " \t\n") + (setq end (point)) + (cond ((< beg end) + (goto-char beg) + (while (search-forward "\n" end t) + (replace-match " ")))) + (setq args (cons (if (> end beg) (buffer-substring beg end)) + args)) + (goto-char next)) + (if (eolp) (forward-char 1)) + (setq texinfo-command-end (point)) + (nreverse args))) + +(defun texinfo-format-parse-defun-args () + (goto-char texinfo-command-end) + (let ((start (point))) + (end-of-line) + (setq texinfo-command-end (1+ (point))) + (let ((marker (move-marker (make-marker) texinfo-command-end))) + (texinfo-format-expand-region start (point)) + (setq texinfo-command-end (marker-position marker)) + (move-marker marker nil)) + (goto-char start) + (let ((args '()) + beg end) + (skip-chars-forward " ") + (while (not (eolp)) + (cond ((looking-at "{") + (setq beg (1+ (point))) + (forward-list 1) + (setq end (1- (point)))) + (t + (setq beg (point)) + (re-search-forward "[\n ]") + (forward-char -1) + (setq end (point)))) + (setq args (cons (buffer-substring beg end) args)) + (skip-chars-forward " ")) + (forward-char 1) + (nreverse args)))) + +(defun texinfo-discard-line () + (goto-char texinfo-command-end) + (skip-chars-forward " \t") + (or (eolp) + (error "Extraneous text at end of command line.")) + (goto-char texinfo-command-start) + (or (bolp) + (error "Extraneous text at beginning of command line.")) + (delete-region (point) (progn (forward-line 1) (point)))) + +(defun texinfo-discard-line-with-args () + (goto-char texinfo-command-start) + (delete-region (point) (progn (forward-line 1) (point)))) + + +;;; @setfilename + +;; Only `texinfo-format-buffer' handles @setfilename with this +;; definition; `texinfo-format-region' handles @setfilename, if any, +;; specially. +(put 'setfilename 'texinfo-format 'texinfo-format-setfilename) +(defun texinfo-format-setfilename () + (let ((arg (texinfo-parse-arg-discard))) + (message "Formatting Info file: %s" arg) + (setq texinfo-format-filename + (file-name-nondirectory (expand-file-name arg))) + (insert "Info file: " + texinfo-format-filename ", -*-Text-*-\n" + ;; Date string removed so that regression testing is easier. + ;; "produced on " + ;; (substring (current-time-string) 8 10) " " + ;; (substring (current-time-string) 4 7) " " + ;; (substring (current-time-string) -4) " " + "produced by `texinfo-format-buffer'\n" + "from file" + (if (buffer-file-name input-buffer) + (concat " `" + (file-name-sans-versions + (file-name-nondirectory + (buffer-file-name input-buffer))) + "'") + (concat "buffer `" (buffer-name input-buffer) "'")) + "\nusing `texinfmt.el' version " + texinfmt-version + ".\n\n"))) + +;;; @node, @menu + +(put 'node 'texinfo-format 'texinfo-format-node) +(put 'nwnode 'texinfo-format 'texinfo-format-node) +(defun texinfo-format-node () + (let* ((args (texinfo-format-parse-line-args)) + (name (nth 0 args)) + (next (nth 1 args)) + (prev (nth 2 args)) + (up (nth 3 args))) + (texinfo-discard-command) + (setq texinfo-last-node name) + (let ((tem (downcase name))) + (if (assoc tem texinfo-node-names) + (error "Duplicate node name: %s" name) + (setq texinfo-node-names (cons (list tem) texinfo-node-names)))) + (setq texinfo-footnote-number 0) + ;; insert "\n\^_" unconditionally since this is what info is looking for + (insert "\n\^_\nFile: " texinfo-format-filename + ", Node: " name) + (if next + (insert ", Next: " next)) + (if prev + (insert ", Prev: " prev)) + (if up + (insert ", Up: " up)) + (insert ?\n) + (setq texinfo-last-node-pos (point)))) + +(put 'menu 'texinfo-format 'texinfo-format-menu) +(defun texinfo-format-menu () + (texinfo-discard-line) + (insert "* Menu:\n\n")) + +(put 'menu 'texinfo-end 'texinfo-discard-command) + + +;;; Cross references + +; @xref {NODE, FNAME, NAME, FILE, DOCUMENT} +; -> *Note FNAME: (FILE)NODE +; If FILE is missing, +; *Note FNAME: NODE +; If FNAME is empty and NAME is present +; *Note NAME: Node +; If both NAME and FNAME are missing +; *Note NODE:: +; texinfo ignores the DOCUMENT argument. +; -> See section <xref to NODE> [NAME, else NODE], page <xref to NODE> +; If FILE is specified, (FILE)NODE is used for xrefs. +; If fifth argument DOCUMENT is specified, produces +; See section <xref to NODE> [NAME, else NODE], page <xref to NODE> +; of DOCUMENT + +; @ref a reference that does not put `See' or `see' in +; the hardcopy and is the same as @xref in Info +(put 'ref 'texinfo-format 'texinfo-format-xref) + +(put 'xref 'texinfo-format 'texinfo-format-xref) +(defun texinfo-format-xref () + (let ((args (texinfo-format-parse-args))) + (texinfo-discard-command) + (insert "*Note ") + (let ((fname (or (nth 1 args) (nth 2 args)))) + (if (null (or fname (nth 3 args))) + (insert (car args) "::") + (insert (or fname (car args)) ": ") + (if (nth 3 args) + (insert "(" (nth 3 args) ")")) + (insert (car args)))))) + +(put 'pxref 'texinfo-format 'texinfo-format-pxref) +(defun texinfo-format-pxref () + (texinfo-format-xref) + (or (save-excursion + (forward-char -2) + (looking-at "::")) + (insert "."))) + +;@inforef{NODE, FNAME, FILE} +;Like @xref{NODE, FNAME,,FILE} in texinfo. +;In Tex, generates "See Info file FILE, node NODE" +(put 'inforef 'texinfo-format 'texinfo-format-inforef) +(defun texinfo-format-inforef () + (let ((args (texinfo-format-parse-args))) + (texinfo-discard-command) + (if (nth 1 args) + (insert "*Note " (nth 1 args) ": (" (nth 2 args) ")" (car args)) + (insert "*Note " "(" (nth 2 args) ")" (car args) "::")))) + + +;;; Section headings + +(put 'majorheading 'texinfo-format 'texinfo-format-chapter) +(put 'chapheading 'texinfo-format 'texinfo-format-chapter) +(put 'ichapter 'texinfo-format 'texinfo-format-chapter) +(put 'chapter 'texinfo-format 'texinfo-format-chapter) +(put 'iappendix 'texinfo-format 'texinfo-format-chapter) +(put 'appendix 'texinfo-format 'texinfo-format-chapter) +(put 'iunnumbered 'texinfo-format 'texinfo-format-chapter) +(put 'top 'texinfo-format 'texinfo-format-chapter) +(put 'unnumbered 'texinfo-format 'texinfo-format-chapter) +(defun texinfo-format-chapter () + (texinfo-format-chapter-1 ?*)) + +(put 'heading 'texinfo-format 'texinfo-format-section) +(put 'isection 'texinfo-format 'texinfo-format-section) +(put 'section 'texinfo-format 'texinfo-format-section) +(put 'iappendixsection 'texinfo-format 'texinfo-format-section) +(put 'appendixsection 'texinfo-format 'texinfo-format-section) +(put 'iappendixsec 'texinfo-format 'texinfo-format-section) +(put 'appendixsec 'texinfo-format 'texinfo-format-section) +(put 'iunnumberedsec 'texinfo-format 'texinfo-format-section) +(put 'unnumberedsec 'texinfo-format 'texinfo-format-section) +(defun texinfo-format-section () + (texinfo-format-chapter-1 ?=)) + +(put 'subheading 'texinfo-format 'texinfo-format-subsection) +(put 'isubsection 'texinfo-format 'texinfo-format-subsection) +(put 'subsection 'texinfo-format 'texinfo-format-subsection) +(put 'iappendixsubsec 'texinfo-format 'texinfo-format-subsection) +(put 'appendixsubsec 'texinfo-format 'texinfo-format-subsection) +(put 'iunnumberedsubsec 'texinfo-format 'texinfo-format-subsection) +(put 'unnumberedsubsec 'texinfo-format 'texinfo-format-subsection) +(defun texinfo-format-subsection () + (texinfo-format-chapter-1 ?-)) + +(put 'subsubheading 'texinfo-format 'texinfo-format-subsubsection) +(put 'isubsubsection 'texinfo-format 'texinfo-format-subsubsection) +(put 'subsubsection 'texinfo-format 'texinfo-format-subsubsection) +(put 'iappendixsubsubsec 'texinfo-format 'texinfo-format-subsubsection) +(put 'appendixsubsubsec 'texinfo-format 'texinfo-format-subsubsection) +(put 'iunnumberedsubsubsec 'texinfo-format 'texinfo-format-subsubsection) +(put 'unnumberedsubsubsec 'texinfo-format 'texinfo-format-subsubsection) +(defun texinfo-format-subsubsection () + (texinfo-format-chapter-1 ?.)) + +(defun texinfo-format-chapter-1 (belowchar) + (let ((arg (texinfo-parse-arg-discard))) + (message "Formatting: %s ... " arg) ; So we can see where we are. + (insert ?\n arg ?\n "@SectionPAD " belowchar ?\n) + (forward-line -2))) + +(put 'SectionPAD 'texinfo-format 'texinfo-format-sectionpad) +(defun texinfo-format-sectionpad () + (let ((str (texinfo-parse-arg-discard))) + (forward-char -1) + (let ((column (current-column))) + (forward-char 1) + (while (> column 0) + (insert str) + (setq column (1- column)))) + (insert ?\n))) + + +;;; Space controlling commands: @. and @:, and the soft hyphen. + +(put '\. 'texinfo-format 'texinfo-format-\.) +(defun texinfo-format-\. () + (texinfo-discard-command) + (insert ".")) + +(put '\: 'texinfo-format 'texinfo-format-\:) +(defun texinfo-format-\: () + (texinfo-discard-command)) + +(put '\- 'texinfo-format 'texinfo-format-soft-hyphen) +(defun texinfo-format-soft-hyphen () + (texinfo-discard-command)) + + +;;; @center, @sp, and @br + +(put 'center 'texinfo-format 'texinfo-format-center) +(defun texinfo-format-center () + (let ((arg (texinfo-parse-expanded-arg))) + (texinfo-discard-command) + (insert arg) + (insert ?\n) + (save-restriction + (goto-char (1- (point))) + (let ((indent-tabs-mode nil)) + (center-line))))) + +(put 'sp 'texinfo-format 'texinfo-format-sp) +(defun texinfo-format-sp () + (let* ((arg (texinfo-parse-arg-discard)) + (num (read arg))) + (insert-char ?\n num))) + +(put 'br 'texinfo-format 'texinfo-format-paragraph-break) +(defun texinfo-format-paragraph-break () + "Force a paragraph break. +If used within a line, follow `@br' with braces." + (texinfo-optional-braces-discard) + ;; insert one return if at end of line; + ;; else insert two returns, to generate a blank line. + (if (= (following-char) ?\n) + (insert ?\n) + (insert-char ?\n 2))) + + +;;; @footnote and @footnotestyle + +; In Texinfo, footnotes are created with the `@footnote' command. +; This command is followed immediately by a left brace, then by the text of +; the footnote, and then by a terminating right brace. The +; template for a footnote is: +; +; @footnote{TEXT} +; +; Info has two footnote styles: +; +; * In the End of node style, all the footnotes for a single node +; are placed at the end of that node. The footnotes are +; separated from the rest of the node by a line of dashes with +; the word `Footnotes' within it. +; +; * In the Separate node style, all the footnotes for a single node +; are placed in an automatically constructed node of their own. + +; Footnote style is specified by the @footnotestyle command, either +; @footnotestyle separate +; or +; @footnotestyle end +; +; The default is separate + +(defvar texinfo-footnote-style "separate" + "Footnote style, either separate or end.") + +(put 'footnotestyle 'texinfo-format 'texinfo-footnotestyle) +(defun texinfo-footnotestyle () + "Specify whether footnotes are at end of node or in separate nodes. +Argument is either end or separate." + (setq texinfo-footnote-style (texinfo-parse-arg-discard))) + +(defvar texinfo-footnote-number) + +(put 'footnote 'texinfo-format 'texinfo-format-footnote) +(defun texinfo-format-footnote () + "Format a footnote in either end of node or separate node style. +The texinfo-footnote-style variable controls which style is used." + (setq texinfo-footnote-number (1+ texinfo-footnote-number)) + (cond ((string= texinfo-footnote-style "end") + (texinfo-format-end-node)) + ((string= texinfo-footnote-style "separate") + (texinfo-format-separate-node)))) + +(defun texinfo-format-separate-node () + "Format footnote in Separate node style, with notes in own node. +The node is constructed automatically." + (let* (start + (arg (texinfo-parse-line-arg)) + (node-name-beginning + (save-excursion + (re-search-backward + "^File: \\w+\\(\\w\\|\\s_\\|\\.\\|,\\)*[ \t]+Node:") + (match-end 0))) + (node-name + (save-excursion + (buffer-substring + (progn (goto-char node-name-beginning) ; skip over node command + (skip-chars-forward " \t") ; and over spaces + (point)) + (if (search-forward + "," + (save-excursion (end-of-line) (point)) t) ; bound search + (1- (point)) + (end-of-line) (point)))))) + (texinfo-discard-command) ; remove or insert whitespace, as needed + (delete-region (save-excursion (skip-chars-backward " \t\n") (point)) + (point)) + (insert (format " (%d) (*Note %s-Footnotes::)" + texinfo-footnote-number node-name)) + (fill-paragraph nil) + (save-excursion + (if (re-search-forward "^@node" nil 'move) + (forward-line -1)) + + ;; two cases: for the first footnote, we must insert a node header; + ;; for the second and subsequent footnotes, we need only insert + ;; the text of the footnote. + + (if (save-excursion + (re-search-backward + (concat node-name "-Footnotes, Up: ") + node-name-beginning + t)) + (progn ; already at least one footnote + (setq start (point)) + (insert (format "\n(%d) %s\n" texinfo-footnote-number arg)) + (fill-region start (point))) + ;; else not yet a footnote + (insert "\n\^_\nFile: " texinfo-format-filename + " Node: " node-name "-Footnotes, Up: " node-name "\n") + (setq start (point)) + (insert (format "\n(%d) %s\n" texinfo-footnote-number arg)) + (fill-region start (point)))))) + +(defun texinfo-format-end-node () + "Format footnote in the End of node style, with notes at end of node." + (let (start + (arg (texinfo-parse-line-arg))) + (texinfo-discard-command) ; remove or insert whitespace, as needed + (delete-region (save-excursion (skip-chars-backward " \t\n") (point)) + (point)) + (insert (format " (%d) " texinfo-footnote-number)) + (fill-paragraph nil) + (save-excursion + (if (search-forward "\n--------- Footnotes ---------\n" nil t) + (progn ; already have footnote, put new one before end of node + (if (re-search-forward "^@node" nil 'move) + (forward-line -1)) + (setq start (point)) + (insert (format "\n(%d) %s\n" texinfo-footnote-number arg)) + (fill-region start (point))) + ;; else no prior footnote + (if (re-search-forward "^@node" nil 'move) + (forward-line -1)) + (insert "\n--------- Footnotes ---------\n") + (setq start (point)) + (insert (format "\n(%d) %s\n" texinfo-footnote-number arg)))))) + + +;;; @itemize, @enumerate, and similar commands + +;; @itemize pushes (itemize "COMMANDS" STARTPOS) on texinfo-stack. +;; @enumerate pushes (enumerate 0 STARTPOS). +;; @item dispatches to the texinfo-item prop of the first elt of the list. +;; For itemize, this puts in and rescans the COMMANDS. +;; For enumerate, this increments the number and puts it in. +;; In either case, it puts a Backspace at the front of the line +;; which marks it not to be indented later. +;; All other lines get indented by 5 when the @end is reached. + +(defvar texinfo-stack-depth 0 + "Count of number of unpopped texinfo-push-stack calls. +Used by @refill indenting command to avoid indenting within lists, etc.") + +(defun texinfo-push-stack (check arg) + (setq texinfo-stack-depth (1+ texinfo-stack-depth)) + (setq texinfo-stack + (cons (list check arg texinfo-command-start) + texinfo-stack))) + +(defun texinfo-pop-stack (check) + (setq texinfo-stack-depth (1- texinfo-stack-depth)) + (if (null texinfo-stack) + (error "Unmatched @end %s" check)) + (if (not (eq (car (car texinfo-stack)) check)) + (error "@end %s matches @%s" + check (car (car texinfo-stack)))) + (prog1 (cdr (car texinfo-stack)) + (setq texinfo-stack (cdr texinfo-stack)))) + +(put 'itemize 'texinfo-format 'texinfo-itemize) +(defun texinfo-itemize () + (texinfo-push-stack + 'itemize + (progn (skip-chars-forward " \t") + (if (eolp) + "@bullet" + (texinfo-parse-line-arg)))) + (texinfo-discard-line-with-args) + (setq fill-column (- fill-column 5))) + +(put 'itemize 'texinfo-end 'texinfo-end-itemize) +(defun texinfo-end-itemize () + (setq fill-column (+ fill-column 5)) + (texinfo-discard-command) + (let ((stacktop + (texinfo-pop-stack 'itemize))) + (texinfo-do-itemize (nth 1 stacktop)))) + +(put 'enumerate 'texinfo-format 'texinfo-enumerate) +(defun texinfo-enumerate () + (texinfo-push-stack + 'enumerate + (progn (skip-chars-forward " \t") + (if (eolp) + 1 + (read (current-buffer))))) + (if (and (symbolp (car (cdr (car texinfo-stack)))) + (> 1 (length (symbol-name (car (cdr (car texinfo-stack))))))) + (error + "@enumerate: Use a number or letter, eg: 1, A, a, 3, B, or d." )) + (texinfo-discard-line-with-args) + (setq fill-column (- fill-column 5))) + +(put 'enumerate 'texinfo-end 'texinfo-end-enumerate) +(defun texinfo-end-enumerate () + (setq fill-column (+ fill-column 5)) + (texinfo-discard-command) + (let ((stacktop + (texinfo-pop-stack 'enumerate))) + (texinfo-do-itemize (nth 1 stacktop)))) + +;; @alphaenumerate never became a standard part of Texinfo +(put 'alphaenumerate 'texinfo-format 'texinfo-alphaenumerate) +(defun texinfo-alphaenumerate () + (texinfo-push-stack 'alphaenumerate (1- ?a)) + (setq fill-column (- fill-column 5)) + (texinfo-discard-line)) + +(put 'alphaenumerate 'texinfo-end 'texinfo-end-alphaenumerate) +(defun texinfo-end-alphaenumerate () + (setq fill-column (+ fill-column 5)) + (texinfo-discard-command) + (let ((stacktop + (texinfo-pop-stack 'alphaenumerate))) + (texinfo-do-itemize (nth 1 stacktop)))) + +;; @capsenumerate never became a standard part of Texinfo +(put 'capsenumerate 'texinfo-format 'texinfo-capsenumerate) +(defun texinfo-capsenumerate () + (texinfo-push-stack 'capsenumerate (1- ?A)) + (setq fill-column (- fill-column 5)) + (texinfo-discard-line)) + +(put 'capsenumerate 'texinfo-end 'texinfo-end-capsenumerate) +(defun texinfo-end-capsenumerate () + (setq fill-column (+ fill-column 5)) + (texinfo-discard-command) + (let ((stacktop + (texinfo-pop-stack 'capsenumerate))) + (texinfo-do-itemize (nth 1 stacktop)))) + +;; At the @end, indent all the lines within the construct +;; except those marked with backspace. FROM says where +;; construct started. +(defun texinfo-do-itemize (from) + (save-excursion + (while (progn (forward-line -1) + (>= (point) from)) + (if (= (following-char) ?\b) + (save-excursion + (delete-char 1) + (end-of-line) + (delete-char 6)) + (if (not (looking-at "[ \t]*$")) + (save-excursion (insert " "))))))) + +(put 'item 'texinfo-format 'texinfo-item) +(put 'itemx 'texinfo-format 'texinfo-item) +(defun texinfo-item () + (funcall (get (car (car texinfo-stack)) 'texinfo-item))) + +(put 'itemize 'texinfo-item 'texinfo-itemize-item) +(defun texinfo-itemize-item () + ;; (texinfo-discard-line) ; Did not handle text on same line as @item. + (delete-region (1+ (point)) (save-excursion (beginning-of-line) (point))) + (if (looking-at "[ \t]*[^ \t\n]+") + ;; Text on same line as @item command. + (insert "\b " (nth 1 (car texinfo-stack)) " \n") + ;; Else text on next line. + (insert "\b " (nth 1 (car texinfo-stack)) " ")) + (forward-line -1)) + +(put 'enumerate 'texinfo-item 'texinfo-enumerate-item) +(defun texinfo-enumerate-item () + (texinfo-discard-line) + (let (enumerating-symbol) + (cond ((integerp (car (cdr (car texinfo-stack)))) + (setq enumerating-symbol (car (cdr (car texinfo-stack)))) + (insert ?\b (format "%3d. " enumerating-symbol) ?\n) + (setcar (cdr (car texinfo-stack)) (1+ enumerating-symbol))) + ((symbolp (car (cdr (car texinfo-stack)))) + (setq enumerating-symbol + (symbol-name (car (cdr (car texinfo-stack))))) + (if (or (equal ?\[ (string-to-char enumerating-symbol)) + (equal ?\{ (string-to-char enumerating-symbol))) + (error + "Too many items in enumerated list; alphabet ends at Z.")) + (insert ?\b (format "%3s. " enumerating-symbol) ?\n) + (setcar (cdr (car texinfo-stack)) + (make-symbol + (char-to-string + (1+ + (string-to-char enumerating-symbol)))))) + (t + (error + "@enumerate: Use a number or letter, eg: 1, A, a, 3, B or d." ))) + (forward-line -1))) + +(put 'alphaenumerate 'texinfo-item 'texinfo-alphaenumerate-item) +(defun texinfo-alphaenumerate-item () + (texinfo-discard-line) + (let ((next (1+ (car (cdr (car texinfo-stack)))))) + (if (> next ?z) + (error "More than 26 items in @alphaenumerate; get a bigger alphabet.")) + (setcar (cdr (car texinfo-stack)) next) + (insert "\b " next ". \n")) + (forward-line -1)) + +(put 'capsenumerate 'texinfo-item 'texinfo-capsenumerate-item) +(defun texinfo-capsenumerate-item () + (texinfo-discard-line) + (let ((next (1+ (car (cdr (car texinfo-stack)))))) + (if (> next ?Z) + (error "More than 26 items in @capsenumerate; get a bigger alphabet.")) + (setcar (cdr (car texinfo-stack)) next) + (insert "\b " next ". \n")) + (forward-line -1)) + + +;;; @table + +; The `@table' command produces two-column tables. + +(put 'table 'texinfo-format 'texinfo-table) +(defun texinfo-table () + (texinfo-push-stack + 'table + (progn (skip-chars-forward " \t") + (if (eolp) + "@asis" + (texinfo-parse-line-arg)))) + (texinfo-discard-line-with-args) + (setq fill-column (- fill-column 5))) + +(put 'table 'texinfo-item 'texinfo-table-item) +(defun texinfo-table-item () + (let ((arg (texinfo-parse-arg-discard)) + (itemfont (car (cdr (car texinfo-stack))))) + (insert ?\b itemfont ?\{ arg "}\n \n")) + (forward-line -2)) + +(put 'table 'texinfo-end 'texinfo-end-table) +(defun texinfo-end-table () + (setq fill-column (+ fill-column 5)) + (texinfo-discard-command) + (let ((stacktop + (texinfo-pop-stack 'table))) + (texinfo-do-itemize (nth 1 stacktop)))) + +;; @description appears to be an undocumented variant on @table that +;; does not require an arg. It fails in texinfo.tex 2.58 and is not +;; part of makeinfo.c The command appears to be a relic of the past. +(put 'description 'texinfo-end 'texinfo-end-table) +(put 'description 'texinfo-format 'texinfo-description) +(defun texinfo-description () + (texinfo-push-stack 'table "@asis") + (setq fill-column (- fill-column 5)) + (texinfo-discard-line)) + + +;;; @ftable, @vtable + +; The `@ftable' and `@vtable' commands are like the `@table' command +; but they also insert each entry in the first column of the table +; into the function or variable index. + +;; Handle the @ftable and @vtable commands: + +(put 'ftable 'texinfo-format 'texinfo-ftable) +(put 'vtable 'texinfo-format 'texinfo-vtable) + +(defun texinfo-ftable () (texinfo-indextable 'ftable)) +(defun texinfo-vtable () (texinfo-indextable 'vtable)) + +(defun texinfo-indextable (table-type) + (texinfo-push-stack table-type (texinfo-parse-arg-discard)) + (setq fill-column (- fill-column 5))) + +;; Handle the @item commands within ftable and vtable: + +(put 'ftable 'texinfo-item 'texinfo-ftable-item) +(put 'vtable 'texinfo-item 'texinfo-vtable-item) + +(defun texinfo-ftable-item () (texinfo-indextable-item 'texinfo-findex)) +(defun texinfo-vtable-item () (texinfo-indextable-item 'texinfo-vindex)) + +(defun texinfo-indextable-item (index-type) + (let ((item (texinfo-parse-arg-discard)) + (itemfont (car (cdr (car texinfo-stack)))) + (indexvar index-type)) + (insert ?\b itemfont ?\{ item "}\n \n") + (set indexvar + (cons + (list item texinfo-last-node) + (symbol-value indexvar))) + (forward-line -2))) + +;; Handle @end ftable, @end vtable + +(put 'ftable 'texinfo-end 'texinfo-end-ftable) +(put 'vtable 'texinfo-end 'texinfo-end-vtable) + +(defun texinfo-end-ftable () (texinfo-end-indextable 'ftable)) +(defun texinfo-end-vtable () (texinfo-end-indextable 'vtable)) + +(defun texinfo-end-indextable (table-type) + (setq fill-column (+ fill-column 5)) + (texinfo-discard-command) + (let ((stacktop + (texinfo-pop-stack table-type))) + (texinfo-do-itemize (nth 1 stacktop)))) + + +;;; @ifinfo, @iftex, @tex, @ifhtml, @html + +(put 'ifinfo 'texinfo-format 'texinfo-discard-line) +(put 'ifinfo 'texinfo-end 'texinfo-discard-command) + +(put 'iftex 'texinfo-format 'texinfo-format-iftex) +(defun texinfo-format-iftex () + (delete-region texinfo-command-start + (progn (re-search-forward "@end iftex[ \t]*\n") + (point)))) + +(put 'ifhtml 'texinfo-format 'texinfo-format-ifhtml) +(defun texinfo-format-ifhtml () + (delete-region texinfo-command-start + (progn (re-search-forward "@end ifhtml[ \t]*\n") + (point)))) + +(put 'tex 'texinfo-format 'texinfo-format-tex) +(defun texinfo-format-tex () + (delete-region texinfo-command-start + (progn (re-search-forward "@end tex[ \t]*\n") + (point)))) + +(put 'html 'texinfo-format 'texinfo-format-html) +(defun texinfo-format-html () + (delete-region texinfo-command-start + (progn (re-search-forward "@end html[ \t]*\n") + (point)))) + + +;;; @titlepage + +(put 'titlepage 'texinfo-format 'texinfo-format-titlepage) +(defun texinfo-format-titlepage () + (delete-region texinfo-command-start + (progn (re-search-forward "@end titlepage[ \t]*\n") + (point)))) + +(put 'endtitlepage 'texinfo-format 'texinfo-discard-line) + +; @titlespec an alternative titling command; ignored by Info + +(put 'titlespec 'texinfo-format 'texinfo-format-titlespec) +(defun texinfo-format-titlespec () + (delete-region texinfo-command-start + (progn (re-search-forward "@end titlespec[ \t]*\n") + (point)))) + +(put 'endtitlespec 'texinfo-format 'texinfo-discard-line) + + +;;; @today + +(put 'today 'texinfo-format 'texinfo-format-today) + +; Produces Day Month Year style of output. eg `1 Jan 1900' +; The `@today{}' command requires a pair of braces, like `@dots{}'. +(defun texinfo-format-today () + (texinfo-parse-arg-discard) + (insert (format "%s %s %s" + (substring (current-time-string) 8 10) + (substring (current-time-string) 4 7) + (substring (current-time-string) -4)))) + + +;;; @ignore + +(put 'ignore 'texinfo-format 'texinfo-format-ignore) +(defun texinfo-format-ignore () + (delete-region texinfo-command-start + (progn (re-search-forward "@end ignore[ \t]*\n") + (point)))) + +(put 'endignore 'texinfo-format 'texinfo-discard-line) + + +;;; Define the Info enclosure command: @definfoenclose + +; A `@definfoenclose' command may be used to define a highlighting +; command for Info, but not for TeX. A command defined using +; `@definfoenclose' marks text by enclosing it in strings that precede +; and follow the text. +; +; Presumably, if you define a command with `@definfoenclose` for Info, +; you will also define the same command in the TeX definitions file, +; `texinfo.tex' in a manner appropriate for typesetting. +; +; Write a `@definfoenclose' command on a line and follow it with three +; arguments separated by commas (commas are used as separators in an +; `@node' line in the same way). The first argument to +; `@definfoenclose' is the @-command name \(without the `@'\); the +; second argument is the Info start delimiter string; and the third +; argument is the Info end delimiter string. The latter two arguments +; enclose the highlighted text in the Info file. A delimiter string +; may contain spaces. Neither the start nor end delimiter is +; required. However, if you do not provide a start delimiter, you +; must follow the command name with two commas in a row; otherwise, +; the Info formatting commands will misinterpret the end delimiter +; string as a start delimiter string. +; +; If you do a @definfoenclose{} on the name of a pre-defined macro (such +; as @emph{}, @strong{}, @tt{}, or @i{}) the enclosure definition will +; override the built-in definition. +; +; An enclosure command defined this way takes one argument in braces. +; +; For example, you can write: +; +; @ifinfo +; @definfoenclose phoo, //, \\ +; @end ifinfo +; +; near the beginning of a Texinfo file at the beginning of the lines +; to define `@phoo' as an Info formatting command that inserts `//' +; before and `\\' after the argument to `@phoo'. You can then write +; `@phoo{bar}' wherever you want `//bar\\' highlighted in Info. +; +; Also, for TeX formatting, you could write +; +; @iftex +; @global@let@phoo=@i +; @end iftex +; +; to define `@phoo' as a command that causes TeX to typeset +; the argument to `@phoo' in italics. +; +; Note that each definition applies to its own formatter: one for TeX, +; the other for texinfo-format-buffer or texinfo-format-region. +; +; Here is another example: write +; +; @definfoenclose headword, , : +; +; near the beginning of the file, to define `@headword' as an Info +; formatting command that inserts nothing before and a colon after the +; argument to `@headword'. + +(put 'definfoenclose 'texinfo-format 'texinfo-define-info-enclosure) +(defun texinfo-define-info-enclosure () + (let* ((args (texinfo-format-parse-line-args)) + (command-name (nth 0 args)) + (beginning-delimiter (or (nth 1 args) "")) + (end-delimiter (or (nth 2 args) ""))) + (texinfo-discard-command) + (setq texinfo-enclosure-list + (cons + (list command-name + (list + beginning-delimiter + end-delimiter)) + texinfo-enclosure-list)))) + + +;;; @var, @code and the like + +(put 'var 'texinfo-format 'texinfo-format-var) +; @sc a small caps font for TeX; formatted as `var' in Info +(put 'sc 'texinfo-format 'texinfo-format-var) +(defun texinfo-format-var () + (insert (upcase (texinfo-parse-arg-discard))) + (goto-char texinfo-command-start)) + +; various noops + +(put 'b 'texinfo-format 'texinfo-format-noop) +(put 'i 'texinfo-format 'texinfo-format-noop) +(put 'r 'texinfo-format 'texinfo-format-noop) +(put 't 'texinfo-format 'texinfo-format-noop) +(put 'w 'texinfo-format 'texinfo-format-noop) +(put 'asis 'texinfo-format 'texinfo-format-noop) +(put 'dmn 'texinfo-format 'texinfo-format-noop) +(put 'key 'texinfo-format 'texinfo-format-noop) +(put 'math 'texinfo-format 'texinfo-format-noop) +(put 'titlefont 'texinfo-format 'texinfo-format-noop) +(defun texinfo-format-noop () + (insert (texinfo-parse-arg-discard)) + (goto-char texinfo-command-start)) + +(put 'cite 'texinfo-format 'texinfo-format-code) +(put 'code 'texinfo-format 'texinfo-format-code) +(put 'file 'texinfo-format 'texinfo-format-code) +(put 'kbd 'texinfo-format 'texinfo-format-code) +(put 'samp 'texinfo-format 'texinfo-format-code) +(defun texinfo-format-code () + (insert "`" (texinfo-parse-arg-discard) "'") + (goto-char texinfo-command-start)) + +(put 'emph 'texinfo-format 'texinfo-format-emph) +(put 'strong 'texinfo-format 'texinfo-format-emph) +(defun texinfo-format-emph () + (insert "*" (texinfo-parse-arg-discard) "*") + (goto-char texinfo-command-start)) + +(put 'dfn 'texinfo-format 'texinfo-format-defn) +(put 'defn 'texinfo-format 'texinfo-format-defn) +(defun texinfo-format-defn () + (insert "\"" (texinfo-parse-arg-discard) "\"") + (goto-char texinfo-command-start)) + +(put 'bullet 'texinfo-format 'texinfo-format-bullet) +(defun texinfo-format-bullet () + "Insert an asterisk. +If used within a line, follow `@bullet' with braces." + (texinfo-optional-braces-discard) + (insert "*")) + + +;;; @example, @lisp, @quotation, @display, @smalllisp, @smallexample + +(put 'display 'texinfo-format 'texinfo-format-example) +(put 'example 'texinfo-format 'texinfo-format-example) +(put 'lisp 'texinfo-format 'texinfo-format-example) +(put 'quotation 'texinfo-format 'texinfo-format-example) +(put 'smallexample 'texinfo-format 'texinfo-format-example) +(put 'smalllisp 'texinfo-format 'texinfo-format-example) +(defun texinfo-format-example () + (texinfo-push-stack 'example nil) + (setq fill-column (- fill-column 5)) + (texinfo-discard-line)) + +(put 'example 'texinfo-end 'texinfo-end-example) +(put 'display 'texinfo-end 'texinfo-end-example) +(put 'lisp 'texinfo-end 'texinfo-end-example) +(put 'quotation 'texinfo-end 'texinfo-end-example) +(put 'smallexample 'texinfo-end 'texinfo-end-example) +(put 'smalllisp 'texinfo-end 'texinfo-end-example) +(defun texinfo-end-example () + (setq fill-column (+ fill-column 5)) + (texinfo-discard-command) + (let ((stacktop + (texinfo-pop-stack 'example))) + (texinfo-do-itemize (nth 1 stacktop)))) + +(put 'exdent 'texinfo-format 'texinfo-format-exdent) +(defun texinfo-format-exdent () + (texinfo-discard-command) + (delete-region (point) + (progn + (skip-chars-forward " ") + (point))) + (insert ?\b) + ;; Cancel out the deletion that texinfo-do-itemize + ;; is going to do at the end of this line. + (save-excursion + (end-of-line) + (insert "\n "))) + + +;;; @cartouche + +; The @cartouche command is a noop in Info; in a printed manual, +; it makes a box with rounded corners. + +(put 'cartouche 'texinfo-format 'texinfo-discard-line) +(put 'cartouche 'texinfo-end 'texinfo-discard-command) + + +;;; @flushleft and @format + +; The @flushleft command left justifies every line but leaves the +; right end ragged. As far as Info is concerned, @flushleft is a +; `do-nothing' command + +; The @format command is similar to @example except that it does not +; indent; this means that in Info, @format is similar to @flushleft. + +(put 'format 'texinfo-format 'texinfo-format-flushleft) +(put 'flushleft 'texinfo-format 'texinfo-format-flushleft) +(defun texinfo-format-flushleft () + (texinfo-discard-line)) + +(put 'format 'texinfo-end 'texinfo-end-flushleft) +(put 'flushleft 'texinfo-end 'texinfo-end-flushleft) +(defun texinfo-end-flushleft () + (texinfo-discard-command)) + + +;;; @flushright + +; The @flushright command right justifies every line but leaves the +; left end ragged. Spaces and tabs at the right ends of lines are +; removed so that visible text lines up on the right side. + +(put 'flushright 'texinfo-format 'texinfo-format-flushright) +(defun texinfo-format-flushright () + (texinfo-push-stack 'flushright nil) + (texinfo-discard-line)) + +(put 'flushright 'texinfo-end 'texinfo-end-flushright) +(defun texinfo-end-flushright () + (texinfo-discard-command) + + (let ((stacktop + (texinfo-pop-stack 'flushright))) + + (texinfo-do-flushright (nth 1 stacktop)))) + +(defun texinfo-do-flushright (from) + (save-excursion + (while (progn (forward-line -1) + (>= (point) from)) + + (beginning-of-line) + (insert + (make-string + (- fill-column + (save-excursion + (end-of-line) + (skip-chars-backward " \t") + (delete-region (point) (progn (end-of-line) (point))) + (current-column))) + ? ))))) + + +;;; @ctrl, @TeX, @copyright, @minus, @dots + +(put 'ctrl 'texinfo-format 'texinfo-format-ctrl) +(defun texinfo-format-ctrl () + (let ((str (texinfo-parse-arg-discard))) + (insert (logand 31 (aref str 0))))) + +(put 'TeX 'texinfo-format 'texinfo-format-TeX) +(defun texinfo-format-TeX () + (texinfo-parse-arg-discard) + (insert "TeX")) + +(put 'copyright 'texinfo-format 'texinfo-format-copyright) +(defun texinfo-format-copyright () + (texinfo-parse-arg-discard) + (insert "(C)")) + +(put 'minus 'texinfo-format 'texinfo-format-minus) +(defun texinfo-format-minus () + "Insert a minus sign. +If used within a line, follow `@minus' with braces." + (texinfo-optional-braces-discard) + (insert "-")) + +(put 'dots 'texinfo-format 'texinfo-format-dots) +(defun texinfo-format-dots () + (texinfo-parse-arg-discard) + (insert "...")) + +(put 'enddots 'texinfo-format 'texinfo-format-enddots) +(defun texinfo-format-enddots () + (texinfo-parse-arg-discard) + (insert "....")) + + +;;; Refilling and indenting: @refill, @paragraphindent, @noindent + +;;; Indent only those paragraphs that are refilled as a result of an +;;; @refill command. + +; * If the value is `asis', do not change the existing indentation at +; the starts of paragraphs. + +; * If the value zero, delete any existing indentation. + +; * If the value is greater than zero, indent each paragraph by that +; number of spaces. + +;;; But do not refill paragraphs with an @refill command that are +;;; preceded by @noindent or are part of a table, list, or deffn. + +(defvar texinfo-paragraph-indent "asis" + "Number of spaces for @refill to indent a paragraph; else to leave as is.") + +(put 'paragraphindent 'texinfo-format 'texinfo-paragraphindent) + +(defun texinfo-paragraphindent () + "Specify the number of spaces for @refill to indent a paragraph. +Default is to leave the number of spaces as is." + (let ((arg (texinfo-parse-arg-discard))) + (if (string= "asis" arg) + (setq texinfo-paragraph-indent "asis") + (setq texinfo-paragraph-indent (string-to-int arg))))) + +(put 'refill 'texinfo-format 'texinfo-format-refill) +(defun texinfo-format-refill () + "Refill paragraph. Also, indent first line as set by @paragraphindent. +Default is to leave paragraph indentation as is." + (texinfo-discard-command) + (forward-paragraph -1) + (if (looking-at "[ \t\n]*$") (forward-line 1)) + ;; Do not indent if an entry in a list, table, or deffn, + ;; or if paragraph is preceded by @noindent. + ;; Otherwise, indent + (cond + ;; delete a @noindent line and do not indent paragraph + ((save-excursion (forward-line -1) + (looking-at "^@noindent")) + (forward-line -1) + (delete-region (point) (progn (forward-line 1) (point)))) + ;; do nothing if "asis" + ((equal texinfo-paragraph-indent "asis")) + ;; do no indenting in list, etc. + ((> texinfo-stack-depth 0)) + ;; otherwise delete existing whitespace and indent + (t + (delete-region (point) (progn (skip-chars-forward " \t") (point))) + (insert (make-string texinfo-paragraph-indent ? )))) + (forward-paragraph 1) + (forward-line -1) + (end-of-line) + ;; Do not fill a section title line with asterisks, hyphens, etc. that + ;; are used to underline it. This could occur if the line following + ;; the underlining is not an index entry and has text within it. + (let* ((previous-paragraph-separate paragraph-separate) + (paragraph-separate + (concat paragraph-separate "\\|[-=.]+\\|\\*\\*+")) + (previous-paragraph-start paragraph-start) + (paragraph-start + (concat paragraph-start "\\|[-=.]+\\|\\*\\*+"))) + (unwind-protect + (fill-paragraph nil) + (setq paragraph-separate previous-paragraph-separate) + (setq paragraph-start previous-paragraph-start)))) + +(put 'noindent 'texinfo-format 'texinfo-noindent) +(defun texinfo-noindent () + (save-excursion + (forward-paragraph 1) + (if (search-backward "@refill" + (save-excursion (forward-line -1) (point)) t) + () ; leave @noindent command so @refill command knows not to indent + ;; else + (texinfo-discard-line)))) + + +;;; Index generation + +(put 'vindex 'texinfo-format 'texinfo-format-vindex) +(defun texinfo-format-vindex () + (texinfo-index 'texinfo-vindex)) + +(put 'cindex 'texinfo-format 'texinfo-format-cindex) +(defun texinfo-format-cindex () + (texinfo-index 'texinfo-cindex)) + +(put 'findex 'texinfo-format 'texinfo-format-findex) +(defun texinfo-format-findex () + (texinfo-index 'texinfo-findex)) + +(put 'pindex 'texinfo-format 'texinfo-format-pindex) +(defun texinfo-format-pindex () + (texinfo-index 'texinfo-pindex)) + +(put 'tindex 'texinfo-format 'texinfo-format-tindex) +(defun texinfo-format-tindex () + (texinfo-index 'texinfo-tindex)) + +(put 'kindex 'texinfo-format 'texinfo-format-kindex) +(defun texinfo-format-kindex () + (texinfo-index 'texinfo-kindex)) + +(defun texinfo-index (indexvar) + (let ((arg (texinfo-parse-expanded-arg))) + (texinfo-discard-command) + (set indexvar + (cons (list arg + texinfo-last-node + ;; Region formatting may not provide last node position. + (if texinfo-last-node-pos + (1+ (count-lines texinfo-last-node-pos (point))) + 1)) + (symbol-value indexvar))))) + +(defconst texinfo-indexvar-alist + '(("cp" . texinfo-cindex) + ("fn" . texinfo-findex) + ("vr" . texinfo-vindex) + ("tp" . texinfo-tindex) + ("pg" . texinfo-pindex) + ("ky" . texinfo-kindex))) + + +;;; @defindex @defcodeindex +(put 'defindex 'texinfo-format 'texinfo-format-defindex) +(put 'defcodeindex 'texinfo-format 'texinfo-format-defindex) + +(defun texinfo-format-defindex () + (let* ((index-name (texinfo-parse-arg-discard)) ; eg: `aa' + (indexing-command (intern (concat index-name "index"))) + (index-formatting-command ; eg: `texinfo-format-aaindex' + (intern (concat "texinfo-format-" index-name "index"))) + (index-alist-name ; eg: `texinfo-aaindex' + (intern (concat "texinfo-" index-name "index")))) + + (set index-alist-name nil) + + (put indexing-command ; eg, aaindex + 'texinfo-format + index-formatting-command) ; eg, texinfo-format-aaindex + + ;; eg: "aa" . texinfo-aaindex + (or (assoc index-name texinfo-indexvar-alist) + (setq texinfo-indexvar-alist + (cons + (cons index-name + index-alist-name) + texinfo-indexvar-alist))) + + (fset index-formatting-command + (list 'lambda 'nil + (list 'texinfo-index + (list 'quote index-alist-name)))))) + + +;;; @synindex @syncodeindex + +(put 'synindex 'texinfo-format 'texinfo-format-synindex) +(put 'syncodeindex 'texinfo-format 'texinfo-format-synindex) + +(defun texinfo-format-synindex () + (let* ((args (texinfo-parse-arg-discard)) + (second (cdr (read-from-string args))) + (joiner (symbol-name (car (read-from-string args)))) + (joined (symbol-name (car (read-from-string args second))))) + + (if (assoc joiner texinfo-short-index-cmds-alist) + (put + (cdr (assoc joiner texinfo-short-index-cmds-alist)) + 'texinfo-format + (or (cdr (assoc joined texinfo-short-index-format-cmds-alist)) + (intern (concat "texinfo-format-" joined "index")))) + (put + (intern (concat joiner "index")) + 'texinfo-format + (or (cdr(assoc joined texinfo-short-index-format-cmds-alist)) + (intern (concat "texinfo-format-" joined "index"))))))) + +(defconst texinfo-short-index-cmds-alist + '(("cp" . cindex) + ("fn" . findex) + ("vr" . vindex) + ("tp" . tindex) + ("pg" . pindex) + ("ky" . kindex))) + +(defconst texinfo-short-index-format-cmds-alist + '(("cp" . texinfo-format-cindex) + ("fn" . texinfo-format-findex) + ("vr" . texinfo-format-vindex) + ("tp" . texinfo-format-tindex) + ("pg" . texinfo-format-pindex) + ("ky" . texinfo-format-kindex))) + + +;;; Sort and index (for VMS) + +;; Sort an index which is in the current buffer between START and END. +;; Used on VMS, where the `sort' utility is not available. +(defun texinfo-sort-region (start end) + (require 'sort) + (save-restriction + (narrow-to-region start end) + (sort-subr nil 'forward-line 'end-of-line 'texinfo-sort-startkeyfun))) + +;; Subroutine for sorting an index. +;; At start of a line, return a string to sort the line under. +(defun texinfo-sort-startkeyfun () + (let ((line + (buffer-substring (point) (save-excursion (end-of-line) (point))))) + ;; Canonicalize whitespace and eliminate funny chars. + (while (string-match "[ \t][ \t]+\\|[^a-z0-9 ]+" line) + (setq line (concat (substring line 0 (match-beginning 0)) + " " + (substring line (match-end 0) (length line))))) + line)) + + +;;; @printindex + +(put 'printindex 'texinfo-format 'texinfo-format-printindex) + +(defun texinfo-format-printindex () + (let ((indexelts (symbol-value + (cdr (assoc (texinfo-parse-arg-discard) + texinfo-indexvar-alist)))) + opoint) + (insert "\n* Menu:\n\n") + (setq opoint (point)) + (texinfo-print-index nil indexelts) + + (if (eq system-type 'vax-vms) + (texinfo-sort-region opoint (point)) + (shell-command-on-region opoint (point) "sort -fd" 1)))) + +(defun texinfo-print-index (file indexelts) + (while indexelts + (if (stringp (car (car indexelts))) + (progn + (insert "* " (car (car indexelts)) ": " ) + (indent-to 32) + (insert + (if file (concat "(" file ")") "") + (nth 1 (car indexelts)) ".") + (indent-to 54) + (insert + (if (nth 2 (car indexelts)) + (format " %d." (nth 2 (car indexelts))) + "") + "\n")) + ;; index entries from @include'd file + (texinfo-print-index (nth 1 (car indexelts)) + (nth 2 (car indexelts)))) + (setq indexelts (cdr indexelts)))) + + +;;; Glyphs: @equiv, @error, etc + +;; @equiv to show that two expressions are equivalent +;; @error to show an error message +;; @expansion to show what a macro expands to +;; @point to show the location of point in an example +;; @print to show what an evaluated expression prints +;; @result to indicate the value returned by an expression + +(put 'equiv 'texinfo-format 'texinfo-format-equiv) +(defun texinfo-format-equiv () + (texinfo-parse-arg-discard) + (insert "==")) + +(put 'error 'texinfo-format 'texinfo-format-error) +(defun texinfo-format-error () + (texinfo-parse-arg-discard) + (insert "error-->")) + +(put 'expansion 'texinfo-format 'texinfo-format-expansion) +(defun texinfo-format-expansion () + (texinfo-parse-arg-discard) + (insert "==>")) + +(put 'point 'texinfo-format 'texinfo-format-point) +(defun texinfo-format-point () + (texinfo-parse-arg-discard) + (insert "-!-")) + +(put 'print 'texinfo-format 'texinfo-format-print) +(defun texinfo-format-print () + (texinfo-parse-arg-discard) + (insert "-|")) + +(put 'result 'texinfo-format 'texinfo-format-result) +(defun texinfo-format-result () + (texinfo-parse-arg-discard) + (insert "=>")) + + +;;; Definition formatting: @deffn, @defun, etc + +;; What definition formatting produces: +;; +;; @deffn category name args... +;; In Info, `Category: name ARGS' +;; In index: name: node. line#. +;; +;; @defvr category name +;; In Info, `Category: name' +;; In index: name: node. line#. +;; +;; @deftp category name attributes... +;; `category name attributes...' Note: @deftp args in lower case. +;; In index: name: node. line#. +;; +;; Specialized function-like or variable-like entity: +;; +;; @defun, @defmac, @defspec, @defvar, @defopt +;; +;; @defun name args In Info, `Function: name ARGS' +;; @defmac name args In Info, `Macro: name ARGS' +;; @defvar name In Info, `Variable: name' +;; etc. +;; In index: name: node. line#. +;; +;; Generalized typed-function-like or typed-variable-like entity: +;; @deftypefn category data-type name args... +;; In Info, `Category: data-type name args...' +;; @deftypevr category data-type name +;; In Info, `Category: data-type name' +;; In index: name: node. line#. +;; +;; Specialized typed-function-like or typed-variable-like entity: +;; @deftypefun data-type name args... +;; In Info, `Function: data-type name ARGS' +;; In index: name: node. line#. +;; +;; @deftypevar data-type name +;; In Info, `Variable: data-type name' +;; In index: name: node. line#. but include args after name!? +;; +;; Generalized object oriented entity: +;; @defop category class name args... +;; In Info, `Category on class: name ARG' +;; In index: name on class: node. line#. +;; +;; @defcv category class name +;; In Info, `Category of class: name' +;; In index: name of class: node. line#. +;; +;; Specialized object oriented entity: +;; @defmethod class name args... +;; In Info, `Method on class: name ARGS' +;; In index: name on class: node. line#. +;; +;; @defivar class name +;; In Info, `Instance variable of class: name' +;; In index: name of class: node. line#. + + +;;; The definition formatting functions + +(defun texinfo-format-defun () + (texinfo-push-stack 'defun nil) + (setq fill-column (- fill-column 5)) + (texinfo-format-defun-1 t)) + +(defun texinfo-end-defun () + (setq fill-column (+ fill-column 5)) + (texinfo-discard-command) + (let ((start (nth 1 (texinfo-pop-stack 'defun)))) + (texinfo-do-itemize start) + ;; Delete extra newline inserted after header. + (save-excursion + (goto-char start) + (delete-char -1)))) + +(defun texinfo-format-defunx () + (texinfo-format-defun-1 nil)) + +(defun texinfo-format-defun-1 (first-p) + (let ((parse-args (texinfo-format-parse-defun-args)) + (texinfo-defun-type (get texinfo-command-name 'texinfo-defun-type))) + (texinfo-discard-command) + ;; Delete extra newline inserted after previous header line. + (if (not first-p) + (delete-char -1)) + (funcall + (get texinfo-command-name 'texinfo-deffn-formatting-property) parse-args) + ;; Insert extra newline so that paragraph filling does not mess + ;; with header line. + (insert "\n\n") + (rplaca (cdr (cdr (car texinfo-stack))) (point)) + (funcall + (get texinfo-command-name 'texinfo-defun-indexing-property) parse-args))) + +;;; Formatting the first line of a definition + +;; @deffn, @defvr, @deftp +(put 'deffn 'texinfo-deffn-formatting-property 'texinfo-format-deffn) +(put 'deffnx 'texinfo-deffn-formatting-property 'texinfo-format-deffn) +(put 'defvr 'texinfo-deffn-formatting-property 'texinfo-format-deffn) +(put 'defvrx 'texinfo-deffn-formatting-property 'texinfo-format-deffn) +(put 'deftp 'texinfo-deffn-formatting-property 'texinfo-format-deffn) +(put 'deftpx 'texinfo-deffn-formatting-property 'texinfo-format-deffn) +(defun texinfo-format-deffn (parsed-args) + ;; Generalized function-like, variable-like, or generic data-type entity: + ;; @deffn category name args... + ;; In Info, `Category: name ARGS' + ;; @deftp category name attributes... + ;; `category name attributes...' Note: @deftp args in lower case. + (let ((category (car parsed-args)) + (name (car (cdr parsed-args))) + (args (cdr (cdr parsed-args)))) + (insert " -- " category ": " name) + (while args + (insert " " + (if (or (= ?& (aref (car args) 0)) + (eq (eval (car texinfo-defun-type)) 'deftp-type)) + (car args) + (upcase (car args)))) + (setq args (cdr args))))) + +;; @defun, @defmac, @defspec, @defvar, @defopt: Specialized, simple +(put 'defun 'texinfo-deffn-formatting-property + 'texinfo-format-specialized-defun) +(put 'defunx 'texinfo-deffn-formatting-property + 'texinfo-format-specialized-defun) +(put 'defmac 'texinfo-deffn-formatting-property + 'texinfo-format-specialized-defun) +(put 'defmacx 'texinfo-deffn-formatting-property + 'texinfo-format-specialized-defun) +(put 'defspec 'texinfo-deffn-formatting-property + 'texinfo-format-specialized-defun) +(put 'defspecx 'texinfo-deffn-formatting-property + 'texinfo-format-specialized-defun) +(put 'defvar 'texinfo-deffn-formatting-property + 'texinfo-format-specialized-defun) +(put 'defvarx 'texinfo-deffn-formatting-property + 'texinfo-format-specialized-defun) +(put 'defopt 'texinfo-deffn-formatting-property + 'texinfo-format-specialized-defun) +(put 'defoptx 'texinfo-deffn-formatting-property + 'texinfo-format-specialized-defun) +(defun texinfo-format-specialized-defun (parsed-args) + ;; Specialized function-like or variable-like entity: + ;; @defun name args In Info, `Function: Name ARGS' + ;; @defmac name args In Info, `Macro: Name ARGS' + ;; @defvar name In Info, `Variable: Name' + ;; Use cdr of texinfo-defun-type to determine category: + (let ((category (car (cdr texinfo-defun-type))) + (name (car parsed-args)) + (args (cdr parsed-args))) + (insert " -- " category ": " name) + (while args + (insert " " + (if (= ?& (aref (car args) 0)) + (car args) + (upcase (car args)))) + (setq args (cdr args))))) + +;; @deftypefn, @deftypevr: Generalized typed +(put 'deftypefn 'texinfo-deffn-formatting-property 'texinfo-format-deftypefn) +(put 'deftypefnx 'texinfo-deffn-formatting-property 'texinfo-format-deftypefn) +(put 'deftypevr 'texinfo-deffn-formatting-property 'texinfo-format-deftypefn) +(put 'deftypevrx 'texinfo-deffn-formatting-property 'texinfo-format-deftypefn) +(defun texinfo-format-deftypefn (parsed-args) + ;; Generalized typed-function-like or typed-variable-like entity: + ;; @deftypefn category data-type name args... + ;; In Info, `Category: data-type name args...' + ;; @deftypevr category data-type name + ;; In Info, `Category: data-type name' + ;; Note: args in lower case, unless modified in command line. + (let ((category (car parsed-args)) + (data-type (car (cdr parsed-args))) + (name (car (cdr (cdr parsed-args)))) + (args (cdr (cdr (cdr parsed-args))))) + (insert " -- " category ": " data-type " " name) + (while args + (insert " " (car args)) + (setq args (cdr args))))) + +;; @deftypefun, @deftypevar: Specialized typed +(put 'deftypefun 'texinfo-deffn-formatting-property 'texinfo-format-deftypefun) +(put 'deftypefunx 'texinfo-deffn-formatting-property + 'texinfo-format-deftypefun) +(put 'deftypevar 'texinfo-deffn-formatting-property 'texinfo-format-deftypefun) +(put 'deftypevarx 'texinfo-deffn-formatting-property + 'texinfo-format-deftypefun) +(defun texinfo-format-deftypefun (parsed-args) + ;; Specialized typed-function-like or typed-variable-like entity: + ;; @deftypefun data-type name args... + ;; In Info, `Function: data-type name ARGS' + ;; @deftypevar data-type name + ;; In Info, `Variable: data-type name' + ;; Note: args in lower case, unless modified in command line. + ;; Use cdr of texinfo-defun-type to determine category: + (let ((category (car (cdr texinfo-defun-type))) + (data-type (car parsed-args)) + (name (car (cdr parsed-args))) + (args (cdr (cdr parsed-args)))) + (insert " -- " category ": " data-type " " name) + (while args + (insert " " (car args)) + (setq args (cdr args))))) + +;; @defop: Generalized object-oriented +(put 'defop 'texinfo-deffn-formatting-property 'texinfo-format-defop) +(put 'defopx 'texinfo-deffn-formatting-property 'texinfo-format-defop) +(defun texinfo-format-defop (parsed-args) + ;; Generalized object oriented entity: + ;; @defop category class name args... + ;; In Info, `Category on class: name ARG' + ;; Note: args in upper case; use of `on' + (let ((category (car parsed-args)) + (class (car (cdr parsed-args))) + (name (car (cdr (cdr parsed-args)))) + (args (cdr (cdr (cdr parsed-args))))) + (insert " -- " category " on " class ": " name) + (while args + (insert " " (upcase (car args))) + (setq args (cdr args))))) + +;; @defcv: Generalized object-oriented +(put 'defcv 'texinfo-deffn-formatting-property 'texinfo-format-defcv) +(put 'defcvx 'texinfo-deffn-formatting-property 'texinfo-format-defcv) +(defun texinfo-format-defcv (parsed-args) + ;; Generalized object oriented entity: + ;; @defcv category class name + ;; In Info, `Category of class: name' + ;; Note: args in upper case; use of `of' + (let ((category (car parsed-args)) + (class (car (cdr parsed-args))) + (name (car (cdr (cdr parsed-args)))) + (args (cdr (cdr (cdr parsed-args))))) + (insert " -- " category " of " class ": " name) + (while args + (insert " " (upcase (car args))) + (setq args (cdr args))))) + +;; @defmethod: Specialized object-oriented +(put 'defmethod 'texinfo-deffn-formatting-property 'texinfo-format-defmethod) +(put 'defmethodx 'texinfo-deffn-formatting-property 'texinfo-format-defmethod) +(defun texinfo-format-defmethod (parsed-args) + ;; Specialized object oriented entity: + ;; @defmethod class name args... + ;; In Info, `Method on class: name ARGS' + ;; Note: args in upper case; use of `on' + ;; Use cdr of texinfo-defun-type to determine category: + (let ((category (car (cdr texinfo-defun-type))) + (class (car parsed-args)) + (name (car (cdr parsed-args))) + (args (cdr (cdr parsed-args)))) + (insert " -- " category " on " class ": " name) + (while args + (insert " " (upcase (car args))) + (setq args (cdr args))))) + +;; @defivar: Specialized object-oriented +(put 'defivar 'texinfo-deffn-formatting-property 'texinfo-format-defivar) +(put 'defivarx 'texinfo-deffn-formatting-property 'texinfo-format-defivar) +(defun texinfo-format-defivar (parsed-args) + ;; Specialized object oriented entity: + ;; @defivar class name + ;; In Info, `Instance variable of class: name' + ;; Note: args in upper case; use of `of' + ;; Use cdr of texinfo-defun-type to determine category: + (let ((category (car (cdr texinfo-defun-type))) + (class (car parsed-args)) + (name (car (cdr parsed-args))) + (args (cdr (cdr parsed-args)))) + (insert " -- " category " of " class ": " name) + (while args + (insert " " (upcase (car args))) + (setq args (cdr args))))) + + +;;; Indexing for definitions + +;; An index entry has three parts: the `entry proper', the node name, and the +;; line number. Depending on the which command is used, the entry is +;; formatted differently: +;; +;; @defun, +;; @defmac, +;; @defspec, +;; @defvar, +;; @defopt all use their 1st argument as the entry-proper +;; +;; @deffn, +;; @defvr, +;; @deftp +;; @deftypefun +;; @deftypevar all use their 2nd argument as the entry-proper +;; +;; @deftypefn, +;; @deftypevr both use their 3rd argument as the entry-proper +;; +;; @defmethod uses its 2nd and 1st arguments as an entry-proper +;; formatted: NAME on CLASS + +;; @defop uses its 3rd and 2nd arguments as an entry-proper +;; formatted: NAME on CLASS +;; +;; @defivar uses its 2nd and 1st arguments as an entry-proper +;; formatted: NAME of CLASS +;; +;; @defcv uses its 3rd and 2nd argument as an entry-proper +;; formatted: NAME of CLASS + +(put 'defun 'texinfo-defun-indexing-property 'texinfo-index-defun) +(put 'defunx 'texinfo-defun-indexing-property 'texinfo-index-defun) +(put 'defmac 'texinfo-defun-indexing-property 'texinfo-index-defun) +(put 'defmacx 'texinfo-defun-indexing-property 'texinfo-index-defun) +(put 'defspec 'texinfo-defun-indexing-property 'texinfo-index-defun) +(put 'defspecx 'texinfo-defun-indexing-property 'texinfo-index-defun) +(put 'defvar 'texinfo-defun-indexing-property 'texinfo-index-defun) +(put 'defvarx 'texinfo-defun-indexing-property 'texinfo-index-defun) +(put 'defopt 'texinfo-defun-indexing-property 'texinfo-index-defun) +(put 'defoptx 'texinfo-defun-indexing-property 'texinfo-index-defun) +(defun texinfo-index-defun (parsed-args) + ;; use 1st parsed-arg as entry-proper + ;; `index-list' will be texinfo-findex or the like + (let ((index-list (get texinfo-command-name 'texinfo-defun-index))) + (set index-list + (cons + ;; Three elements: entry-proper, node-name, line-number + (list + (car parsed-args) + texinfo-last-node + ;; Region formatting may not provide last node position. + (if texinfo-last-node-pos + (1+ (count-lines texinfo-last-node-pos (point))) + 1)) + (symbol-value index-list))))) + +(put 'deffn 'texinfo-defun-indexing-property 'texinfo-index-deffn) +(put 'deffnx 'texinfo-defun-indexing-property 'texinfo-index-deffn) +(put 'defvr 'texinfo-defun-indexing-property 'texinfo-index-deffn) +(put 'defvrx 'texinfo-defun-indexing-property 'texinfo-index-deffn) +(put 'deftp 'texinfo-defun-indexing-property 'texinfo-index-deffn) +(put 'deftpx 'texinfo-defun-indexing-property 'texinfo-index-deffn) +(put 'deftypefun 'texinfo-defun-indexing-property 'texinfo-index-deffn) +(put 'deftypefunx 'texinfo-defun-indexing-property 'texinfo-index-deffn) +(put 'deftypevar 'texinfo-defun-indexing-property 'texinfo-index-deffn) +(put 'deftypevarx 'texinfo-defun-indexing-property 'texinfo-index-deffn) +(defun texinfo-index-deffn (parsed-args) + ;; use 2nd parsed-arg as entry-proper + ;; `index-list' will be texinfo-findex or the like + (let ((index-list (get texinfo-command-name 'texinfo-defun-index))) + (set index-list + (cons + ;; Three elements: entry-proper, node-name, line-number + (list + (car (cdr parsed-args)) + texinfo-last-node + ;; Region formatting may not provide last node position. + (if texinfo-last-node-pos + (1+ (count-lines texinfo-last-node-pos (point))) + 1)) + (symbol-value index-list))))) + +(put 'deftypefn 'texinfo-defun-indexing-property 'texinfo-index-deftypefn) +(put 'deftypefnx 'texinfo-defun-indexing-property 'texinfo-index-deftypefn) +(put 'deftypevr 'texinfo-defun-indexing-property 'texinfo-index-deftypefn) +(put 'deftypevrx 'texinfo-defun-indexing-property 'texinfo-index-deftypefn) +(defun texinfo-index-deftypefn (parsed-args) + ;; use 3rd parsed-arg as entry-proper + ;; `index-list' will be texinfo-findex or the like + (let ((index-list (get texinfo-command-name 'texinfo-defun-index))) + (set index-list + (cons + ;; Three elements: entry-proper, node-name, line-number + (list + (car (cdr (cdr parsed-args))) + texinfo-last-node + ;; Region formatting may not provide last node position. + (if texinfo-last-node-pos + (1+ (count-lines texinfo-last-node-pos (point))) + 1)) + (symbol-value index-list))))) + +(put 'defmethod 'texinfo-defun-indexing-property 'texinfo-index-defmethod) +(put 'defmethodx 'texinfo-defun-indexing-property 'texinfo-index-defmethod) +(defun texinfo-index-defmethod (parsed-args) + ;; use 2nd on 1st parsed-arg as entry-proper + ;; `index-list' will be texinfo-findex or the like + (let ((index-list (get texinfo-command-name 'texinfo-defun-index))) + (set index-list + (cons + ;; Three elements: entry-proper, node-name, line-number + (list + (format "%s on %s" + (car (cdr parsed-args)) + (car parsed-args)) + texinfo-last-node + ;; Region formatting may not provide last node position. + (if texinfo-last-node-pos + (1+ (count-lines texinfo-last-node-pos (point))) + 1)) + (symbol-value index-list))))) + +(put 'defop 'texinfo-defun-indexing-property 'texinfo-index-defop) +(put 'defopx 'texinfo-defun-indexing-property 'texinfo-index-defop) +(defun texinfo-index-defop (parsed-args) + ;; use 3rd on 2nd parsed-arg as entry-proper + ;; `index-list' will be texinfo-findex or the like + (let ((index-list (get texinfo-command-name 'texinfo-defun-index))) + (set index-list + (cons + ;; Three elements: entry-proper, node-name, line-number + (list + (format "%s on %s" + (car (cdr (cdr parsed-args))) + (car (cdr parsed-args))) + texinfo-last-node + ;; Region formatting may not provide last node position. + (if texinfo-last-node-pos + (1+ (count-lines texinfo-last-node-pos (point))) + 1)) + (symbol-value index-list))))) + +(put 'defivar 'texinfo-defun-indexing-property 'texinfo-index-defivar) +(put 'defivarx 'texinfo-defun-indexing-property 'texinfo-index-defivar) +(defun texinfo-index-defivar (parsed-args) + ;; use 2nd of 1st parsed-arg as entry-proper + ;; `index-list' will be texinfo-findex or the like + (let ((index-list (get texinfo-command-name 'texinfo-defun-index))) + (set index-list + (cons + ;; Three elements: entry-proper, node-name, line-number + (list + (format "%s of %s" + (car (cdr parsed-args)) + (car parsed-args)) + texinfo-last-node + ;; Region formatting may not provide last node position. + (if texinfo-last-node-pos + (1+ (count-lines texinfo-last-node-pos (point))) + 1)) + (symbol-value index-list))))) + +(put 'defcv 'texinfo-defun-indexing-property 'texinfo-index-defcv) +(put 'defcvx 'texinfo-defun-indexing-property 'texinfo-index-defcv) +(defun texinfo-index-defcv (parsed-args) + ;; use 3rd of 2nd parsed-arg as entry-proper + ;; `index-list' will be texinfo-findex or the like + (let ((index-list (get texinfo-command-name 'texinfo-defun-index))) + (set index-list + (cons + ;; Three elements: entry-proper, node-name, line-number + (list + (format "%s of %s" + (car (cdr (cdr parsed-args))) + (car (cdr parsed-args))) + texinfo-last-node + ;; Region formatting may not provide last node position. + (if texinfo-last-node-pos + (1+ (count-lines texinfo-last-node-pos (point))) + 1)) + (symbol-value index-list))))) + + +;;; Properties for definitions + +;; Each definition command has six properties: +;; +;; 1. texinfo-deffn-formatting-property to format definition line +;; 2. texinfo-defun-indexing-property to create index entry +;; 3. texinfo-format formatting command +;; 4. texinfo-end end formatting command +;; 5. texinfo-defun-type type of deffn to format +;; 6. texinfo-defun-index type of index to use +;; +;; The `x' forms of each definition command are used for the second +;; and subsequent header lines. + +;; The texinfo-deffn-formatting-property and texinfo-defun-indexing-property +;; are listed just before the appropriate formatting and indexing commands. + +(put 'deffn 'texinfo-format 'texinfo-format-defun) +(put 'deffnx 'texinfo-format 'texinfo-format-defunx) +(put 'deffn 'texinfo-end 'texinfo-end-defun) +(put 'deffn 'texinfo-defun-type '('deffn-type nil)) +(put 'deffnx 'texinfo-defun-type '('deffn-type nil)) +(put 'deffn 'texinfo-defun-index 'texinfo-findex) +(put 'deffnx 'texinfo-defun-index 'texinfo-findex) + +(put 'defun 'texinfo-format 'texinfo-format-defun) +(put 'defunx 'texinfo-format 'texinfo-format-defunx) +(put 'defun 'texinfo-end 'texinfo-end-defun) +(put 'defun 'texinfo-defun-type '('defun-type "Function")) +(put 'defunx 'texinfo-defun-type '('defun-type "Function")) +(put 'defun 'texinfo-defun-index 'texinfo-findex) +(put 'defunx 'texinfo-defun-index 'texinfo-findex) + +(put 'defmac 'texinfo-format 'texinfo-format-defun) +(put 'defmacx 'texinfo-format 'texinfo-format-defunx) +(put 'defmac 'texinfo-end 'texinfo-end-defun) +(put 'defmac 'texinfo-defun-type '('defun-type "Macro")) +(put 'defmacx 'texinfo-defun-type '('defun-type "Macro")) +(put 'defmac 'texinfo-defun-index 'texinfo-findex) +(put 'defmacx 'texinfo-defun-index 'texinfo-findex) + +(put 'defspec 'texinfo-format 'texinfo-format-defun) +(put 'defspecx 'texinfo-format 'texinfo-format-defunx) +(put 'defspec 'texinfo-end 'texinfo-end-defun) +(put 'defspec 'texinfo-defun-type '('defun-type "Special form")) +(put 'defspecx 'texinfo-defun-type '('defun-type "Special form")) +(put 'defspec 'texinfo-defun-index 'texinfo-findex) +(put 'defspecx 'texinfo-defun-index 'texinfo-findex) + +(put 'defvr 'texinfo-format 'texinfo-format-defun) +(put 'defvrx 'texinfo-format 'texinfo-format-defunx) +(put 'defvr 'texinfo-end 'texinfo-end-defun) +(put 'defvr 'texinfo-defun-type '('deffn-type nil)) +(put 'defvrx 'texinfo-defun-type '('deffn-type nil)) +(put 'defvr 'texinfo-defun-index 'texinfo-vindex) +(put 'defvrx 'texinfo-defun-index 'texinfo-vindex) + +(put 'defvar 'texinfo-format 'texinfo-format-defun) +(put 'defvarx 'texinfo-format 'texinfo-format-defunx) +(put 'defvar 'texinfo-end 'texinfo-end-defun) +(put 'defvar 'texinfo-defun-type '('defun-type "Variable")) +(put 'defvarx 'texinfo-defun-type '('defun-type "Variable")) +(put 'defvar 'texinfo-defun-index 'texinfo-vindex) +(put 'defvarx 'texinfo-defun-index 'texinfo-vindex) + +(put 'defconst 'texinfo-format 'texinfo-format-defun) +(put 'defconstx 'texinfo-format 'texinfo-format-defunx) +(put 'defconst 'texinfo-end 'texinfo-end-defun) +(put 'defconst 'texinfo-defun-type '('defun-type "Constant")) +(put 'defconstx 'texinfo-defun-type '('defun-type "Constant")) +(put 'defconst 'texinfo-defun-index 'texinfo-vindex) +(put 'defconstx 'texinfo-defun-index 'texinfo-vindex) + +(put 'defcmd 'texinfo-format 'texinfo-format-defun) +(put 'defcmdx 'texinfo-format 'texinfo-format-defunx) +(put 'defcmd 'texinfo-end 'texinfo-end-defun) +(put 'defcmd 'texinfo-defun-type '('defun-type "Command")) +(put 'defcmdx 'texinfo-defun-type '('defun-type "Command")) +(put 'defcmd 'texinfo-defun-index 'texinfo-findex) +(put 'defcmdx 'texinfo-defun-index 'texinfo-findex) + +(put 'defopt 'texinfo-format 'texinfo-format-defun) +(put 'defoptx 'texinfo-format 'texinfo-format-defunx) +(put 'defopt 'texinfo-end 'texinfo-end-defun) +(put 'defopt 'texinfo-defun-type '('defun-type "User Option")) +(put 'defoptx 'texinfo-defun-type '('defun-type "User Option")) +(put 'defopt 'texinfo-defun-index 'texinfo-vindex) +(put 'defoptx 'texinfo-defun-index 'texinfo-vindex) + +(put 'deftp 'texinfo-format 'texinfo-format-defun) +(put 'deftpx 'texinfo-format 'texinfo-format-defunx) +(put 'deftp 'texinfo-end 'texinfo-end-defun) +(put 'deftp 'texinfo-defun-type '('deftp-type nil)) +(put 'deftpx 'texinfo-defun-type '('deftp-type nil)) +(put 'deftp 'texinfo-defun-index 'texinfo-tindex) +(put 'deftpx 'texinfo-defun-index 'texinfo-tindex) + +;;; Object-oriented stuff is a little hairier. + +(put 'defop 'texinfo-format 'texinfo-format-defun) +(put 'defopx 'texinfo-format 'texinfo-format-defunx) +(put 'defop 'texinfo-end 'texinfo-end-defun) +(put 'defop 'texinfo-defun-type '('defop-type nil)) +(put 'defopx 'texinfo-defun-type '('defop-type nil)) +(put 'defop 'texinfo-defun-index 'texinfo-findex) +(put 'defopx 'texinfo-defun-index 'texinfo-findex) + +(put 'defmethod 'texinfo-format 'texinfo-format-defun) +(put 'defmethodx 'texinfo-format 'texinfo-format-defunx) +(put 'defmethod 'texinfo-end 'texinfo-end-defun) +(put 'defmethod 'texinfo-defun-type '('defmethod-type "Method")) +(put 'defmethodx 'texinfo-defun-type '('defmethod-type "Method")) +(put 'defmethod 'texinfo-defun-index 'texinfo-findex) +(put 'defmethodx 'texinfo-defun-index 'texinfo-findex) + +(put 'defcv 'texinfo-format 'texinfo-format-defun) +(put 'defcvx 'texinfo-format 'texinfo-format-defunx) +(put 'defcv 'texinfo-end 'texinfo-end-defun) +(put 'defcv 'texinfo-defun-type '('defop-type nil)) +(put 'defcvx 'texinfo-defun-type '('defop-type nil)) +(put 'defcv 'texinfo-defun-index 'texinfo-vindex) +(put 'defcvx 'texinfo-defun-index 'texinfo-vindex) + +(put 'defivar 'texinfo-format 'texinfo-format-defun) +(put 'defivarx 'texinfo-format 'texinfo-format-defunx) +(put 'defivar 'texinfo-end 'texinfo-end-defun) +(put 'defivar 'texinfo-defun-type '('defmethod-type "Instance variable")) +(put 'defivarx 'texinfo-defun-type '('defmethod-type "Instance variable")) +(put 'defivar 'texinfo-defun-index 'texinfo-vindex) +(put 'defivarx 'texinfo-defun-index 'texinfo-vindex) + +;;; Typed functions and variables + +(put 'deftypefn 'texinfo-format 'texinfo-format-defun) +(put 'deftypefnx 'texinfo-format 'texinfo-format-defunx) +(put 'deftypefn 'texinfo-end 'texinfo-end-defun) +(put 'deftypefn 'texinfo-defun-type '('deftypefn-type nil)) +(put 'deftypefnx 'texinfo-defun-type '('deftypefn-type nil)) +(put 'deftypefn 'texinfo-defun-index 'texinfo-findex) +(put 'deftypefnx 'texinfo-defun-index 'texinfo-findex) + +(put 'deftypefun 'texinfo-format 'texinfo-format-defun) +(put 'deftypefunx 'texinfo-format 'texinfo-format-defunx) +(put 'deftypefun 'texinfo-end 'texinfo-end-defun) +(put 'deftypefun 'texinfo-defun-type '('deftypefun-type "Function")) +(put 'deftypefunx 'texinfo-defun-type '('deftypefun-type "Function")) +(put 'deftypefun 'texinfo-defun-index 'texinfo-findex) +(put 'deftypefunx 'texinfo-defun-index 'texinfo-findex) + +(put 'deftypevr 'texinfo-format 'texinfo-format-defun) +(put 'deftypevrx 'texinfo-format 'texinfo-format-defunx) +(put 'deftypevr 'texinfo-end 'texinfo-end-defun) +(put 'deftypevr 'texinfo-defun-type '('deftypefn-type nil)) +(put 'deftypevrx 'texinfo-defun-type '('deftypefn-type nil)) +(put 'deftypevr 'texinfo-defun-index 'texinfo-vindex) +(put 'deftypevrx 'texinfo-defun-index 'texinfo-vindex) + +(put 'deftypevar 'texinfo-format 'texinfo-format-defun) +(put 'deftypevarx 'texinfo-format 'texinfo-format-defunx) +(put 'deftypevar 'texinfo-end 'texinfo-end-defun) +(put 'deftypevar 'texinfo-defun-type '('deftypevar-type "Variable")) +(put 'deftypevarx 'texinfo-defun-type '('deftypevar-type "Variable")) +(put 'deftypevar 'texinfo-defun-index 'texinfo-vindex) +(put 'deftypevarx 'texinfo-defun-index 'texinfo-vindex) + + +;;; @set, @clear, @ifset, @ifclear + +;; If a flag is set with @set FLAG, then text between @ifset and @end +;; ifset is formatted normally, but if the flag is is cleared with +;; @clear FLAG, then the text is not formatted; it is ignored. + +;; If a flag is cleared with @clear FLAG, then text between @ifclear +;; and @end ifclear is formatted normally, but if the flag is is set with +;; @set FLAG, then the text is not formatted; it is ignored. @ifclear +;; is the opposite of @ifset. + +;; If a flag is set to a string with @set FLAG, +;; replace @value{FLAG} with the string. +;; If a flag with a value is cleared, +;; @value{FLAG} is invalid, +;; as if there had never been any @set FLAG previously. + +(put 'clear 'texinfo-format 'texinfo-clear) +(defun texinfo-clear () + "Clear the value of the flag." + (let* ((arg (texinfo-parse-arg-discard)) + (flag (car (read-from-string arg))) + (value (substring arg (cdr (read-from-string arg))))) + (put flag 'texinfo-whether-setp 'flag-cleared) + (put flag 'texinfo-set-value ""))) + +(put 'set 'texinfo-format 'texinfo-set) +(defun texinfo-set () + "Set the value of the flag, optionally to a string. +The command `@set foo This is a string.' +sets flag foo to the value: `This is a string.' +The command `@value{foo}' expands to the value." + (let* ((arg (texinfo-parse-arg-discard)) + (flag (car (read-from-string arg))) + (value (substring arg (cdr (read-from-string arg))))) + (put flag 'texinfo-whether-setp 'flag-set) + (put flag 'texinfo-set-value value))) + +(put 'value 'texinfo-format 'texinfo-value) +(defun texinfo-value () + "Insert the string to which the flag is set. +The command `@set foo This is a string.' +sets flag foo to the value: `This is a string.' +The command `@value{foo}' expands to the value." + (let ((arg (texinfo-parse-arg-discard))) + (cond ((and + (eq (get (car (read-from-string arg)) 'texinfo-whether-setp) + 'flag-set) + (get (car (read-from-string arg)) 'texinfo-set-value)) + (insert (get (car (read-from-string arg)) 'texinfo-set-value))) + ((eq (get (car (read-from-string arg)) 'texinfo-whether-setp) + 'flag-cleared) + (insert (format "{No value for \"%s\"}" arg))) + ((eq (get (car (read-from-string arg)) 'texinfo-whether-setp) nil) + (insert (format "{No value for \"%s\"}" arg)))))) + +(put 'ifset 'texinfo-end 'texinfo-discard-command) +(put 'ifset 'texinfo-format 'texinfo-if-set) +(defun texinfo-if-set () + "If set, continue formatting; else do not format region up to @end ifset" + (let ((arg (texinfo-parse-arg-discard))) + (cond + ((eq (get (car (read-from-string arg)) 'texinfo-whether-setp) + 'flag-set) + ;; Format the text (i.e., do not remove it); do nothing here. + ()) + ((eq (get (car (read-from-string arg)) 'texinfo-whether-setp) + 'flag-cleared) + ;; Clear region (i.e., cause the text to be ignored). + (delete-region texinfo-command-start + (progn (re-search-forward "@end ifset[ \t]*\n") + (point)))) + ((eq (get (car (read-from-string arg)) 'texinfo-whether-setp) + nil) + ;; In this case flag is neither set nor cleared. + ;; Act as if set, i.e. do nothing. + ())))) + +(put 'ifclear 'texinfo-end 'texinfo-discard-command) +(put 'ifclear 'texinfo-format 'texinfo-if-clear) +(defun texinfo-if-clear () + "If clear, continue formatting; if set, do not format up to @end ifset" + (let ((arg (texinfo-parse-arg-discard))) + (cond + ((eq (get (car (read-from-string arg)) 'texinfo-whether-setp) + 'flag-set) + ;; Clear region (i.e., cause the text to be ignored). + (delete-region texinfo-command-start + (progn (re-search-forward "@end ifclear[ \t]*\n") + (point)))) + ((eq (get (car (read-from-string arg)) 'texinfo-whether-setp) + 'flag-cleared) + ;; Format the text (i.e., do not remove it); do nothing here. + ()) + ((eq (get (car (read-from-string arg)) 'texinfo-whether-setp) + nil) + ;; In this case flag is neither set nor cleared. + ;; Act as if clear, i.e. do nothing. + ())))) + + +;;; Process included files: `@include' command + +;; Updated 19 October 1990 +;; In the original version, include files were ignored by Info but +;; incorporated in to the printed manual. To make references to the +;; included file, the Texinfo source file has to refer to the included +;; files using the `(filename)nodename' format for refering to other +;; Info files. Also, the included files had to be formatted on their +;; own. It was just like they were another file. + +;; Currently, include files are inserted into the buffer that is +;; formatted for Info. If large, the resulting info file is split and +;; tagified. For current include files to work, the master menu must +;; refer to all the nodes, and the highest level nodes in the include +;; files must have the correct next, prev, and up pointers. + +;; The included file may have an @setfilename and even an @settitle, +;; but not an `\input texinfo' line. + +;; Updated 24 March 1993 +;; In order for @raisesections and @lowersections to work, included +;; files must be inserted into the buffer holding the outer file +;; before other Info formatting takes place. So @include is no longer +;; is treated like other @-commands. +(put 'include 'texinfo-format 'texinfo-format-noop) + +; Original definition: +; (defun texinfo-format-include () +; (let ((filename (texinfo-parse-arg-discard)) +; (default-directory input-directory) +; subindex) +; (setq subindex +; (save-excursion +; (progn (find-file +; (cond ((file-readable-p (concat filename ".texinfo")) +; (concat filename ".texinfo")) +; ((file-readable-p (concat filename ".texi")) +; (concat filename ".texi")) +; ((file-readable-p (concat filename ".tex")) +; (concat filename ".tex")) +; ((file-readable-p filename) +; filename) +; (t (error "@include'd file %s not found" +; filename)))) +; (texinfo-format-buffer-1)))) +; (texinfo-subindex 'texinfo-vindex (car subindex) (nth 1 subindex)) +; (texinfo-subindex 'texinfo-findex (car subindex) (nth 2 subindex)) +; (texinfo-subindex 'texinfo-cindex (car subindex) (nth 3 subindex)) +; (texinfo-subindex 'texinfo-pindex (car subindex) (nth 4 subindex)) +; (texinfo-subindex 'texinfo-tindex (car subindex) (nth 5 subindex)) +; (texinfo-subindex 'texinfo-kindex (car subindex) (nth 6 subindex)))) +; +;(defun texinfo-subindex (indexvar file content) +; (set indexvar (cons (list 'recurse file content) +; (symbol-value indexvar)))) + +; Second definition: +; (put 'include 'texinfo-format 'texinfo-format-include) +; (defun texinfo-format-include () +; (let ((filename (concat input-directory +; (texinfo-parse-arg-discard))) +; (default-directory input-directory)) +; (message "Reading: %s" filename) +; (save-excursion +; (save-restriction +; (narrow-to-region +; (point) +; (+ (point) (car (cdr (insert-file-contents filename))))) +; (goto-char (point-min)) +; (texinfo-append-refill) +; (texinfo-format-convert (point-min) (point-max)))) +; (setq last-input-buffer input-buffer) ; to bypass setfilename +; )) + + +;;; Numerous commands do nothing in Texinfo + +;; These commands are defined in texinfo.tex for printed output. + +(put 'bye 'texinfo-format 'texinfo-discard-line) +(put 'c 'texinfo-format 'texinfo-discard-line-with-args) +(put 'comment 'texinfo-format 'texinfo-discard-line-with-args) +(put 'contents 'texinfo-format 'texinfo-discard-line-with-args) +(put 'finalout 'texinfo-format 'texinfo-discard-line) +(put 'group 'texinfo-end 'texinfo-discard-line-with-args) +(put 'group 'texinfo-format 'texinfo-discard-line-with-args) +(put 'headings 'texinfo-format 'texinfo-discard-line-with-args) +(put 'hsize 'texinfo-format 'texinfo-discard-line-with-args) +(put 'itemindent 'texinfo-format 'texinfo-discard-line-with-args) +(put 'lispnarrowing 'texinfo-format 'texinfo-discard-line-with-args) +(put 'need 'texinfo-format 'texinfo-discard-line-with-args) +(put 'nopara 'texinfo-format 'texinfo-discard-line-with-args) +(put 'page 'texinfo-format 'texinfo-discard-line-with-args) +(put 'parindent 'texinfo-format 'texinfo-discard-line-with-args) +(put 'setchapternewpage 'texinfo-format 'texinfo-discard-line-with-args) +(put 'setq 'texinfo-format 'texinfo-discard-line-with-args) +(put 'settitle 'texinfo-format 'texinfo-discard-line-with-args) +(put 'setx 'texinfo-format 'texinfo-discard-line-with-args) +(put 'shortcontents 'texinfo-format 'texinfo-discard-line-with-args) +(put 'shorttitlepage 'texinfo-format 'texinfo-discard-line-with-args) +(put 'smallbook 'texinfo-format 'texinfo-discard-line) +(put 'summarycontents 'texinfo-format 'texinfo-discard-line-with-args) + + +;;; Some commands cannot be handled + +(defun texinfo-unsupported () + (error "%s is not handled by texinfo" + (buffer-substring texinfo-command-start texinfo-command-end))) + +;;; Batch formatting + +(defun batch-texinfo-format () + "Runs texinfo-format-buffer on the files remaining on the command line. +Must be used only with -batch, and kills emacs on completion. +Each file will be processed even if an error occurred previously. +For example, invoke + \"emacs -batch -funcall batch-texinfo-format $docs/ ~/*.texinfo\"." + (if (not noninteractive) + (error "batch-texinfo-format may only be used -batch.")) + (let ((version-control t) + (auto-save-default nil) + (find-file-run-dired nil) + (kept-old-versions 259259) + (kept-new-versions 259259)) + (let ((error 0) + file + (files ())) + (while command-line-args-left + (setq file (expand-file-name (car command-line-args-left))) + (cond ((not (file-exists-p file)) + (message ">> %s does not exist!" file) + (setq error 1 + command-line-args-left (cdr command-line-args-left))) + ((file-directory-p file) + (setq command-line-args-left + (nconc (directory-files file) + (cdr command-line-args-left)))) + (t + (setq files (cons file files) + command-line-args-left (cdr command-line-args-left))))) + (while files + (setq file (car files) + files (cdr files)) + (condition-case err + (progn + (if buffer-file-name (kill-buffer (current-buffer))) + (find-file file) + (buffer-disable-undo (current-buffer)) + (set-buffer-modified-p nil) + (texinfo-mode) + (message "texinfo formatting %s..." file) + (texinfo-format-buffer nil) + (if (buffer-modified-p) + (progn (message "Saving modified %s" (buffer-file-name)) + (save-buffer)))) + (error + (message ">> Error: %s" (prin1-to-string err)) + (message ">> point at") + (let ((s (buffer-substring (point) + (min (+ (point) 100) + (point-max)))) + (tem 0)) + (while (setq tem (string-match "\n+" s tem)) + (setq s (concat (substring s 0 (match-beginning 0)) + "\n>> " + (substring s (match-end 0))) + tem (1+ tem))) + (message ">> %s" s)) + (setq error 1)))) + (kill-emacs error)))) + + +;;; Place `provide' at end of file. +(provide 'texinfmt) + +;;; texinfmt.el ends here. diff --git a/gnu/usr.bin/texinfo/emacs/texinfo.el b/gnu/usr.bin/texinfo/emacs/texinfo.el new file mode 100644 index 00000000000..2ed5a3291d1 --- /dev/null +++ b/gnu/usr.bin/texinfo/emacs/texinfo.el @@ -0,0 +1,757 @@ +;;;; texinfo.el--major mode for editing Texinfo files. +;; Copyright (C) 1985, '88, '89, +;; '90, '91, '92, '93 Free Software Foundation, Inc. + +;;; Author: Robert J. Chassell +;;; Maintainer: FSF + +;;; This file is part of GNU Emacs. + +;; GNU Emacs is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + +;;; Autoloads: + +(autoload 'makeinfo-region + "makeinfo" + "Make Info file from region of current Texinfo file, and switch to it. + +This command does not offer the `next-error' feature since it would +apply to a temporary file, not the original; use the `makeinfo-buffer' +command to gain use of `next-error'." + t nil) + +(autoload 'makeinfo-buffer + "makeinfo" + "Make Info file from current buffer. + +Use the \\[next-error] command to move to the next error +\(if there are errors\)." + t nil) + +(autoload 'kill-compilation + "compile" + "Kill the process made by the \\[compile] command." + t nil) + +(autoload 'makeinfo-recenter-compilation-buffer + "makeinfo" + "Redisplay `*compilation*' buffer so most recent output can be seen. +The last line of the buffer is displayed on +line LINE of the window, or centered if LINE is nil." + t nil) + +(autoload 'texinfo-make-menu + "texnfo-upd" + "Without any prefix argument, make or update a menu. +Make the menu for the section enclosing the node found following point. + +Non-nil argument (prefix, if interactive) means make or update menus +for nodes within or part of the marked region. + +Whenever a menu exists, and is being updated, the descriptions that +are associated with node names in the pre-existing menu are +incorporated into the new menu. Otherwise, the nodes' section titles +are inserted as descriptions." + t nil) + +(autoload 'texinfo-all-menus-update + "texnfo-upd" + "Update every regular menu in a Texinfo file. +Remove pre-existing master menu, if there is one. + +If called with a non-nil argument, this function first updates all the +nodes in the buffer before updating the menus." + t nil) + +(autoload 'texinfo-master-menu + "texnfo-upd" + "Make a master menu for a whole Texinfo file. +Non-nil argument (prefix, if interactive) means first update all +existing nodes and menus. Remove pre-existing master menu, if there is one. + +This function creates a master menu that follows the top node. The +master menu includes every entry from all the other menus. It +replaces any existing ordinary menu that follows the top node. + +If called with a non-nil argument, this function first updates all the +menus in the buffer (incorporating descriptions from pre-existing +menus) before it constructs the master menu. + +The function removes the detailed part of an already existing master +menu. This action depends on the pre-exisitng master menu using the +standard `texinfo-master-menu-header'. + +The master menu has the following format, which is adapted from the +recommendation in the Texinfo Manual: + + * The first part contains the major nodes in the Texinfo file: the + nodes for the chapters, chapter-like sections, and the major + appendices. This includes the indices, so long as they are in + chapter-like sections, such as unnumbered sections. + + * The second and subsequent parts contain a listing of the other, + lower level menus, in order. This way, an inquirer can go + directly to a particular node if he or she is searching for + specific information. + +Each of the menus in the detailed node listing is introduced by the +title of the section containing the menu." + t nil) + +(autoload 'texinfo-indent-menu-description + "texnfo-upd" + "Indent every description in menu following point to COLUMN. +Non-nil argument (prefix, if interactive) means indent every +description in every menu in the region. Does not indent second and +subsequent lines of a multi-line description." + t nil) + +(autoload 'texinfo-insert-node-lines + "texnfo-upd" + "Insert missing `@node' lines in region of Texinfo file. +Non-nil argument (prefix, if interactive) means also to insert the +section titles as node names; and also to insert the section titles as +node names in pre-existing @node lines that lack names." + t nil) + +(autoload 'texinfo-start-menu-description + "texnfo-upd" + "In this menu entry, insert the node's section title as a description. +Position point at beginning of description ready for editing. +Do not insert a title if the line contains an existing description. + +You will need to edit the inserted text since a useful description +complements the node name rather than repeats it as a title does." + t nil) + +(autoload 'texinfo-multiple-files-update + "texnfo-upd" + "Update first node pointers in each file included in OUTER-FILE; +create or update main menu in the outer file that refers to such nodes. +This does not create or update menus or pointers within the included files. + +With optional MAKE-MASTER-MENU argument (prefix arg, if interactive), +insert a master menu in OUTER-FILE. This does not create or update +menus or pointers within the included files. + +With optional UPDATE-EVERYTHING argument (numeric prefix arg, if +interactive), update all the menus and all the `Next', `Previous', and +`Up' pointers of all the files included in OUTER-FILE before inserting +a master menu in OUTER-FILE. + +The command also updates the `Top' level node pointers of OUTER-FILE. + +Notes: + + * this command does NOT save any files--you must save the + outer file and any modified, included files. + + * except for the `Top' node, this command does NOT handle any + pre-existing nodes in the outer file; hence, indices must be + enclosed in an included file. + +Requirements: + + * each of the included files must contain exactly one highest + hierarchical level node, + * this highest node must be the first node in the included file, + * each highest hierarchical level node must be of the same type. + +Thus, normally, each included file contains one, and only one, +chapter." + t nil) + + +;;; Code: + +;;; Don't you dare insert any `require' calls at top level in this file--rms. + +;;; Syntax table + +(defvar texinfo-mode-syntax-table nil) + +(if texinfo-mode-syntax-table + nil + (setq texinfo-mode-syntax-table (make-syntax-table)) + (modify-syntax-entry ?\" " " texinfo-mode-syntax-table) + (modify-syntax-entry ?\\ " " texinfo-mode-syntax-table) + (modify-syntax-entry ?@ "\\" texinfo-mode-syntax-table) + (modify-syntax-entry ?\^q "\\" texinfo-mode-syntax-table) + (modify-syntax-entry ?\[ "(]" texinfo-mode-syntax-table) + (modify-syntax-entry ?\] ")[" texinfo-mode-syntax-table) + (modify-syntax-entry ?{ "(}" texinfo-mode-syntax-table) + (modify-syntax-entry ?} "){" texinfo-mode-syntax-table) + (modify-syntax-entry ?\' "w" texinfo-mode-syntax-table)) + +(defvar texinfo-font-lock-keywords + (list + '("^\\(@c\\|@comment\\)[ \t].*" . font-lock-comment-face) ;comments + "@\\(@\\|[^}\t \n{]+\\)" ;commands + '("^\\(*.*\\)[\t ]*$" 1 font-lock-function-name-face t) ;menu items + '("@\\(emph\\|strong\\|b\\|i\\){\\([^}]+\\)" 2 font-lock-comment-face t) + '("@\\(file\\|kbd\\|key\\){\\([^}]+\\)" 2 font-lock-string-face t) + '("@\\(samp\\|code\\|var\\){\\([^}]+\\)" 2 font-lock-function-name-face t) + '("@\\(xref\\|pxref\\){\\([^}]+\\)" 2 font-lock-keyword-face t) + '("@end *\\([a-zA-Z0-9]+\\)[ \t]*$" 1 font-lock-function-name-face t) + '("@item \\(.*\\)$" 1 font-lock-function-name-face t) + '("\\$\\([^$]*\\)\\$" 1 font-lock-string-face t) + ) + "Additional expressions to highlight in TeXinfo mode.") + +;;; Keybindings +(defvar texinfo-mode-map nil) + +;;; Keys common both to Texinfo mode and to TeX shell. + +(defun texinfo-define-common-keys (keymap) + "Define the keys both in Texinfo mode and in the texinfo-tex-shell." + (define-key keymap "\C-c\C-t\C-k" 'tex-kill-job) + (define-key keymap "\C-c\C-t\C-x" 'texinfo-quit-job) + (define-key keymap "\C-c\C-t\C-l" 'tex-recenter-output-buffer) + (define-key keymap "\C-c\C-t\C-d" 'texinfo-delete-from-print-queue) + (define-key keymap "\C-c\C-t\C-q" 'tex-show-print-queue) + (define-key keymap "\C-c\C-t\C-p" 'texinfo-tex-print) + (define-key keymap "\C-c\C-t\C-i" 'texinfo-texindex) + + (define-key keymap "\C-c\C-t\C-r" 'texinfo-tex-region) + (define-key keymap "\C-c\C-t\C-b" 'texinfo-tex-buffer)) + +;; Mode documentation displays commands in reverse order +;; from how they are listed in the texinfo-mode-map. + +(if texinfo-mode-map + nil + (setq texinfo-mode-map (make-sparse-keymap)) + + ;; bindings for `texnfo-tex.el' + (texinfo-define-common-keys texinfo-mode-map) + + ;; bindings for `makeinfo.el' + (define-key texinfo-mode-map "\C-c\C-m\C-k" 'kill-compilation) + (define-key texinfo-mode-map "\C-c\C-m\C-l" + 'makeinfo-recenter-compilation-buffer) + (define-key texinfo-mode-map "\C-c\C-m\C-r" 'makeinfo-region) + (define-key texinfo-mode-map "\C-c\C-m\C-b" 'makeinfo-buffer) + + ; Bindings for texinfmt.el. + (define-key texinfo-mode-map "\C-c\C-e\C-r" 'texinfo-format-region) + (define-key texinfo-mode-map "\C-c\C-e\C-b" 'texinfo-format-buffer) + + ;; bindings for updating nodes and menus + + (define-key texinfo-mode-map "\C-c\C-um" 'texinfo-master-menu) + + (define-key texinfo-mode-map "\C-c\C-u\C-m" 'texinfo-make-menu) + (define-key texinfo-mode-map "\C-c\C-u\C-n" 'texinfo-update-node) + (define-key texinfo-mode-map "\C-c\C-u\C-e" 'texinfo-every-node-update) + (define-key texinfo-mode-map "\C-c\C-u\C-a" 'texinfo-all-menus-update) + + (define-key texinfo-mode-map "\C-c\C-s" 'texinfo-show-structure) + + (define-key texinfo-mode-map "\C-c}" 'up-list) + (define-key texinfo-mode-map "\C-c{" 'texinfo-insert-braces) + + ;; bindings for inserting strings + + (define-key texinfo-mode-map "\C-c\C-c\C-d" 'texinfo-start-menu-description) + + (define-key texinfo-mode-map "\C-c\C-cv" 'texinfo-insert-@var) + (define-key texinfo-mode-map "\C-c\C-ct" 'texinfo-insert-@table) + (define-key texinfo-mode-map "\C-c\C-cs" 'texinfo-insert-@samp) + (define-key texinfo-mode-map "\C-c\C-co" 'texinfo-insert-@noindent) + (define-key texinfo-mode-map "\C-c\C-cn" 'texinfo-insert-@node) + (define-key texinfo-mode-map "\C-c\C-ck" 'texinfo-insert-@kbd) + (define-key texinfo-mode-map "\C-c\C-ci" 'texinfo-insert-@item) + (define-key texinfo-mode-map "\C-c\C-cf" 'texinfo-insert-@file) + (define-key texinfo-mode-map "\C-c\C-cx" 'texinfo-insert-@example) + (define-key texinfo-mode-map "\C-c\C-ce" 'texinfo-insert-@end) + (define-key texinfo-mode-map "\C-c\C-cd" 'texinfo-insert-@dfn) + (define-key texinfo-mode-map "\C-c\C-cc" 'texinfo-insert-@code)) + + +;;; Texinfo mode + +(defvar texinfo-chapter-level-regexp + "chapter\\|unnumbered \\|appendix \\|majorheading\\|chapheading" + "Regular expression matching Texinfo chapter-level headings. +This does not match `@node' and does not match the `@top' command.") + +;;;###autoload +(defun texinfo-mode () + "Major mode for editing Texinfo files. + + It has these extra commands: +\\{texinfo-mode-map} + + These are files that are used as input for TeX to make printed manuals +and also to be turned into Info files with \\[makeinfo-buffer] or +the `makeinfo' program. These files must be written in a very restricted and +modified version of TeX input format. + + Editing commands are like text-mode except that the syntax table is +set up so expression commands skip Texinfo bracket groups. To see +what the Info version of a region of the Texinfo file will look like, +use \\[makeinfo-region], which runs `makeinfo' on the current region. + + You can show the structure of a Texinfo file with \\[texinfo-show-structure]. +This command shows the structure of a Texinfo file by listing the +lines with the @-sign commands for @chapter, @section, and the like. +These lines are displayed in another window called the *Occur* window. +In that window, you can position the cursor over one of the lines and +use \\[occur-mode-goto-occurrence], to jump to the corresponding spot +in the Texinfo file. + + In addition, Texinfo mode provides commands that insert various +frequently used @-sign commands into the buffer. You can use these +commands to save keystrokes. And you can insert balanced braces with +\\[texinfo-insert-braces] and later use the command \\[up-list] to +move forward past the closing brace. + +Also, Texinfo mode provides functions for automatically creating or +updating menus and node pointers. These functions + + * insert the `Next', `Previous' and `Up' pointers of a node, + * insert or update the menu for a section, and + * create a master menu for a Texinfo source file. + +Here are the functions: + + texinfo-update-node \\[texinfo-update-node] + texinfo-every-node-update \\[texinfo-every-node-update] + texinfo-sequential-node-update + + texinfo-make-menu \\[texinfo-make-menu] + texinfo-all-menus-update \\[texinfo-all-menus-update] + texinfo-master-menu + + texinfo-indent-menu-description (column &optional region-p) + +The `texinfo-column-for-description' variable specifies the column to +which menu descriptions are indented. + +Passed an argument (a prefix argument, if interactive), the +`texinfo-update-node' and `texinfo-make-menu' functions do their jobs +in the region. + +To use the updating commands, you must structure your Texinfo file +hierarchically, such that each `@node' line, with the exception of the +Top node, is accompanied by some kind of section line, such as an +`@chapter' or `@section' line. + +If the file has a `top' node, it must be called `top' or `Top' and +be the first node in the file. + +Entering Texinfo mode calls the value of text-mode-hook, and then the +value of texinfo-mode-hook." + (interactive) + (text-mode) + (setq mode-name "Texinfo") + (setq major-mode 'texinfo-mode) + (use-local-map texinfo-mode-map) + (set-syntax-table texinfo-mode-syntax-table) + (make-local-variable 'page-delimiter) + (setq page-delimiter + (concat + "^@node [ \t]*[Tt]op\\|^@\\(" + texinfo-chapter-level-regexp + "\\)")) + (make-local-variable 'require-final-newline) + (setq require-final-newline t) + (make-local-variable 'indent-tabs-mode) + (setq indent-tabs-mode nil) + (make-local-variable 'paragraph-separate) + (setq paragraph-separate (concat "\b\\|@[a-zA-Z]*[ \n]\\|" paragraph-separate)) + (make-local-variable 'paragraph-start) + (setq paragraph-start (concat "\b\\|@[a-zA-Z]*[ \n]\\|" paragraph-start)) + (make-local-variable 'fill-column) + (setq fill-column 72) + (make-local-variable 'comment-start) + (setq comment-start "@c ") + (make-local-variable 'comment-start-skip) + (setq comment-start-skip "@c +") + (make-local-variable 'words-include-escapes) + (setq words-include-escapes t) + (make-local-variable 'font-lock-defaults) + (setq font-lock-defaults '(texinfo-font-lock-keywords)) + (make-local-variable 'tex-start-of-header) + (setq tex-start-of-header "%**start") + (make-local-variable 'tex-end-of-header) + (setq tex-end-of-header "%**end") + (run-hooks 'text-mode-hook 'texinfo-mode-hook)) + + +;;; Insert string commands + +(defconst texinfo-environment-regexp + "^@\\(f?table\\|enumerate\\|itemize\ +\\|ifhtml\\|ifinfo\\|iftex\\|ifset\\|ifclear\ +\\|example\\|quotation\\|lisp\\|smallexample\\|smalllisp\\|display\\|format\ +\\|flushleft\\|flushright\\|ignore\\|group\\|tex\\|html\\|cartouche\\|menu\ +\\|titlepage\\|end\\|def[a-z]*[a-wyz]\\>\\)" + "Regexp for environment-like Texinfo list commands. +Subexpression 1 is what goes into the corresponding `@end' statement.") + +(defun texinfo-insert-@end () + "Insert the matching `@end' for the last Texinfo command that needs one." + (interactive) + (let ((depth 1) string) + (save-excursion + (while (and (> depth 0) + (re-search-backward texinfo-environment-regexp nil t) + (if (looking-at "@end") + (setq depth (1+ depth)) + (setq depth (1- depth))))) + (looking-at texinfo-environment-regexp) + (if (zerop depth) + (setq string + (buffer-substring (match-beginning 1) + (match-end 1))))) + (insert "@end ") + (if string (insert string "\n")))) + +;; The following insert commands accept a prefix arg N, which is the +;; number of words (actually s-exprs) that should be surrounded by +;; braces. Thus you can first paste a variable name into a .texinfo +;; buffer, then say C-u 1 C-c C-c v at the beginning of the just +;; pasted variable name to put @var{...} *around* the variable name. +;; Operate on previous word or words with negative arg. + +;; These commands use texinfo-insert-@-with-arg +(defun texinfo-insert-@-with-arg (string &optional arg) + (if arg + (progn + (setq arg (prefix-numeric-value arg)) + (if (< arg 0) + (progn + (skip-chars-backward " \t\n\r\f") + (save-excursion + (forward-sexp arg) + (insert "@" string "{")) + (insert "}")) + (skip-chars-forward " \t\n\r\f") + (insert "@" string "{") + (forward-sexp arg) + (insert "}"))) + (insert "@" string "{}") + (backward-char))) + +(defun texinfo-insert-braces () + "Make a pair of braces and be poised to type inside of them. +Use \\[up-list] to move forward out of the braces." + (interactive) + (insert "{}") + (backward-char)) + +(defun texinfo-insert-@code (&optional arg) + "Insert a `@code{...}' command in a Texinfo buffer. +A numeric argument says how many words the braces should surround. +The default is not to surround any existing words with the braces." + (interactive "P") + (texinfo-insert-@-with-arg "code" arg)) + +(defun texinfo-insert-@dfn (&optional arg) + "Insert a `@dfn{...}' command in a Texinfo buffer. +A numeric argument says how many words the braces should surround. +The default is not to surround any existing words with the braces." + (interactive "P") + (texinfo-insert-@-with-arg "dfn" arg)) + +(defun texinfo-insert-@example () + "Insert the string `@example' in a Texinfo buffer." + (interactive) + (insert "@example\n")) + +(defun texinfo-insert-@file (&optional arg) + "Insert a `@file{...}' command in a Texinfo buffer. +A numeric argument says how many words the braces should surround. +The default is not to surround any existing words with the braces." + (interactive "P") + (texinfo-insert-@-with-arg "file" arg)) + +(defun texinfo-insert-@item () + "Insert the string `@item' in a Texinfo buffer." + (interactive) + (insert "@item") + (newline)) + +(defun texinfo-insert-@kbd (&optional arg) + "Insert a `@kbd{...}' command in a Texinfo buffer. +A numeric argument says how many words the braces should surround. +The default is not to surround any existing words with the braces." + (interactive "P") + (texinfo-insert-@-with-arg "kbd" arg)) + +(defun texinfo-insert-@node () + "Insert the string `@node' in a Texinfo buffer. +This also inserts on the following line a comment indicating +the order of arguments to @node." + (interactive) + (insert "@node \n@comment node-name, next, previous, up") + (forward-line -1) + (forward-char 6)) + +(defun texinfo-insert-@noindent () + "Insert the string `@noindent' in a Texinfo buffer." + (interactive) + (insert "@noindent\n")) + +(defun texinfo-insert-@samp (&optional arg) + "Insert a `@samp{...}' command in a Texinfo buffer. +A numeric argument says how many words the braces should surround. +The default is not to surround any existing words with the braces." + (interactive "P") + (texinfo-insert-@-with-arg "samp" arg)) + +(defun texinfo-insert-@table (&optional arg) + "Insert the string `@table' in a Texinfo buffer." + (interactive "P") + (insert "@table ")) + +(defun texinfo-insert-@var (&optional arg) + "Insert a `@var{}' command in a Texinfo buffer. +A numeric argument says how many words the braces should surround. +The default is not to surround any existing words with the braces." + (interactive "P") + (texinfo-insert-@-with-arg "var" arg)) + +;;; Texinfo file structure + +(defun texinfo-show-structure (&optional nodes-too) + "Show the structure of a Texinfo file. +List the lines in the file that begin with the @-sign commands for +@chapter, @section, and the like. + +With optional argument (prefix if interactive), list both the lines +with @-sign commands for @chapter, @section, and the like, and list +@node lines. + +Lines with structuring commands beginning in them are displayed in +another buffer named `*Occur*'. In that buffer, you can move point to +one of those lines and then use \\<occur-mode-map>\\[occur-mode-goto-occurrence], +to jump to the corresponding spot in the Texinfo source file." + + (interactive "P") + (require 'texnfo-upd) + (save-excursion + (goto-char (point-min)) + (if nodes-too + (occur (concat "\\(^@node\\)\\|" texinfo-section-types-regexp)) + (occur texinfo-section-types-regexp))) + (pop-to-buffer "*Occur*") + (goto-char (point-min)) + (flush-lines "-----") + ;; Now format the "*Occur*" buffer to show the structure. + ;; Thanks to ceder@signum.se (Per Cederqvist) + (goto-char (point-max)) + (let ((margin 5)) + (while (re-search-backward "^ *[0-9]*:" nil 0) + (re-search-forward ":") + (setq margin + (cond + ((looking-at + (concat "@\\(" texinfo-chapter-level-regexp "\\)")) 5) + ;; ((looking-at "@chapter ") 5) + ;; ((looking-at "@unnumbered ") 5) + ;; ((looking-at "@appendix ") 5) + ;; ((looking-at "@majorheading ") 5) + ;; ((looking-at "@chapheading ") 5) + + ((looking-at + (concat "@\\(" texinfo-section-level-regexp "\\)")) 9) + ;; ((looking-at "@section ") 9) + ;; ((looking-at "@unnumberedsec ") 9) + ;; ((looking-at "@appendixsec ") 9) + ;; ((looking-at "@heading ") 9) + + ((looking-at + (concat "@\\(" texinfo-subsection-level-regexp "\\)")) 13) + ;; ((looking-at "@subsection ") 13) + ;; ((looking-at "@unnumberedsubsec ") 13) + ;; ((looking-at "@appendixsubsec ") 13) + ;; ((looking-at "@subheading ") 13) + + ((looking-at + (concat "@\\(" texinfo-subsubsection-level-regexp "\\)")) 17) + ;; ((looking-at "@subsubsection ") 17) + ;; ((looking-at "@unnumberedsubsubsec ") 17) + ;; ((looking-at "@appendixsubsubsec ") 17) + ;; ((looking-at "@subsubheading ") 17) + (t margin))) + (indent-to-column margin) + (beginning-of-line)))) + +;;; The tex and print function definitions: + +(defvar texinfo-texi2dvi-command "texi2dvi" + "*Command used by `texinfo-tex-buffer' to run TeX and texindex on a buffer.") + +(defvar texinfo-tex-command "tex" + "*Command used by `texinfo-tex-region' to run TeX on a region.") + +(defvar texinfo-texindex-command "texindex" + "*Command used by `texinfo-texindex' to sort unsorted index files.") + +(defvar texinfo-delete-from-print-queue-command "lprm" + "*Command string used to delete a job from the line printer queue. +Command is used by \\[texinfo-delete-from-print-queue] based on +number provided by a previous \\[tex-show-print-queue] +command.") + +(defvar texinfo-tex-trailer "@bye" + "String appended after a region sent to TeX by `texinfo-tex-region'.") + +(defun texinfo-tex-region (beg end) + "Run TeX on the current region. +This works by writing a temporary file (`tex-zap-file') in the directory +that is the value of `tex-directory', then running TeX on that file. + +The first line of the buffer is copied to the +temporary file; and if the buffer has a header, it is written to the +temporary file before the region itself. The buffer's header is all lines +between the strings defined by `tex-start-of-header' and `tex-end-of-header' +inclusive. The header must start in the first 100 lines. + +The value of `texinfo-tex-trailer' is appended to the temporary file after the region." + (interactive "r") + (require 'tex-mode) + (if (get-buffer "*tex-shell*") + (tex-kill-job) + (tex-start-shell)) + (or tex-zap-file (setq tex-zap-file (make-temp-name "#tz"))) + (let ((tex-out-file (concat tex-zap-file ".tex")) + (temp-buffer (get-buffer-create " tex-Output-Buffer")) + (zap-directory + (file-name-as-directory (expand-file-name tex-directory)))) + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (forward-line 100) + (let ((search-end (point)) + (hbeg (point-min)) (hend (point-min)) + (default-directory zap-directory)) + (goto-char (point-min)) + + ;; Copy first line, the `\input texinfo' line, to temp file + (write-region (point) + (save-excursion (end-of-line) (point)) + tex-out-file nil nil) + + ;; Don't copy first line twice if region includes it. + (forward-line 1) + (if (< beg (point)) (setq beg (point))) + + ;; Initialize the temp file with either the header or nothing + (if (search-forward tex-start-of-header search-end t) + (progn + (beginning-of-line) + (setq hbeg (point)) ; Mark beginning of header. + (if (search-forward tex-end-of-header nil t) + (progn (beginning-of-line) + (setq hend (point))) ; Mark end of header. + (setq hbeg (point-min))))) ; Else no header. + + ;; Copy header to temp file. + (write-region (min hbeg beg) hend tex-out-file t nil) + + ;; Copy region to temp file. + (write-region (max beg hend) end tex-out-file t nil)) + + ;; This is a kludge to insert the tex-trailer into the tex-out-file. + ;; We have to create a special buffer in which to insert + ;; the tex-trailer first because there is no function with + ;; which to append a literal string directly to a file. + (let ((local-tex-trailer texinfo-tex-trailer)) + (set-buffer temp-buffer) + (erase-buffer) + ;; make sure trailer isn't hidden by a comment + (insert-string "\n") + (if local-tex-trailer (insert-string local-tex-trailer)) + (tex-set-buffer-directory temp-buffer zap-directory) + (write-region (point-min) (point-max) tex-out-file t nil)) + +;;; The following is sufficient in Emacs 19. +;;; (write-region (concat "\n" texinfo-tex-trailer) nil +;;; tex-out-file t nil) + )) + + (tex-set-buffer-directory "*tex-shell*" zap-directory) + (tex-send-command tex-shell-cd-command zap-directory) + (tex-send-command texinfo-tex-command tex-out-file)) + (tex-recenter-output-buffer 0)) + +(defun texinfo-tex-buffer () + "Run TeX on visited file, once or twice, to make a correct `.dvi' file." + (interactive) + + ;; Make sure TeX shell is running. + (require 'tex-mode) + (if (get-buffer "*tex-shell*") + (quit-process (get-process "tex-shell") t) + (tex-start-shell)) + + (cond ((null buffer-file-name) + (error "Buffer not visiting any file!")) + ((buffer-modified-p) + (error "Buffer has been modified since last saved!"))) + + (setq tex-zap-file buffer-file-name) + + (tex-send-command tex-shell-cd-command (file-name-directory tex-zap-file)) + + (tex-send-command texinfo-texi2dvi-command tex-zap-file) + + (tex-recenter-output-buffer 0)) + +(defun texinfo-texindex () + "Run `texindex' on unsorted index files. +The index files are made by \\[texinfo-tex-region] or \\[texinfo-tex-buffer]. +This runs the shell command defined by `texinfo-texindex-command'." + (interactive) + (require 'tex-mode) + (tex-send-command texinfo-texindex-command (concat tex-zap-file ".??")) + (tex-recenter-output-buffer nil)) + +(defun texinfo-tex-print () + "Print `.dvi' file made by \\[texinfo-tex-region] or \\[texinfo-tex-buffer]. +This runs the shell command defined by `tex-dvi-print-command'." + (interactive) + (require 'tex-mode) + (tex-send-command tex-dvi-print-command (concat tex-zap-file ".dvi")) + (tex-recenter-output-buffer nil)) + +(defun texinfo-quit-job () + "Quit currently running TeX job, by sending an `x' to it." + (interactive) + (if (not (get-process "tex-shell")) + (error "No TeX shell running")) + (tex-send-command "x")) + +(defun texinfo-delete-from-print-queue (job-number) + "Delete job from the line printer spooling queue. +You are prompted for the job number (use a number shown by a previous +\\[texinfo-show-print-queue] command)." + (interactive "nPrinter job number for deletion: ") + (require 'tex-mode) + (if (tex-shell-running) + (tex-kill-job) + (tex-start-shell)) + (tex-send-command texinfo-delete-from-print-queue-command job-number) + (tex-recenter-output-buffer nil)) + +(provide 'texinfo) + +;;; texinfo.el ends here diff --git a/gnu/usr.bin/texinfo/emacs/texnfo-tex.el b/gnu/usr.bin/texinfo/emacs/texnfo-tex.el new file mode 100644 index 00000000000..d419f289f7f --- /dev/null +++ b/gnu/usr.bin/texinfo/emacs/texnfo-tex.el @@ -0,0 +1,346 @@ +;;;; texnfo-tex.el + +;;; Texinfo mode TeX and hardcopy printing commands. + +;; These commands are for running TeX on a region of a Texinfo file in +;; GNU Emacs, or on the whole buffer, and for printing the resulting +;; DVI file. + +;;; Version 2.07 22 October 1991 +;;; Robert J. Chassell +;;; Please send bug reports to: bug-texinfo@prep.ai.mit.edu + +;;; Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc. + + +;;; This file is part of GNU Emacs. + +;; GNU Emacs is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 1, or (at your option) +;; any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + +;;; The Texinfo mode TeX related commands are: + +; texinfo-tex-region to run tex on the current region. +; texinfo-tex-buffer to run tex on the current buffer. +; texinfo-texindex to sort unsorted index files. +; texinfo-tex-print to print the .dvi file made by tex. +; texinfo-kill-tex-job to kill the currently running tex job. +; texinfo-recenter-tex-output-buffer to redisplay tex output buffer. +; texinfo-show-tex-print-queue to show the print queue. + + +;;; Keys common both to Texinfo mode and to TeX shell. + +;; Defined in `texinfo.el' +; (defun texinfo-define-common-keys (keymap) +; "Define the keys both in Texinfo mode and in the texinfo-tex-shell." +; (define-key keymap "\C-c\C-t\C-k" 'texinfo-kill-tex-job) +; (define-key keymap "\C-c\C-t\C-x" 'texinfo-quit-tex-job) +; (define-key keymap "\C-c\C-t\C-l" 'texinfo-recenter-tex-output-buffer) +; (define-key keymap "\C-c\C-t\C-d" 'texinfo-delete-from-tex-print-queue) +; (define-key keymap "\C-c\C-t\C-q" 'texinfo-show-tex-print-queue) +; (define-key keymap "\C-c\C-t\C-p" 'texinfo-tex-print) +; (define-key keymap "\C-c\C-t\C-i" 'texinfo-texindex) +; (define-key keymap "\C-c\C-t\C-r" 'texinfo-tex-region) +; (define-key keymap "\C-c\C-t\C-b" 'texinfo-tex-buffer)) + +;; See also texinfo-tex-start-shell. +;; The following is executed in the `texinfo.el' file +;(texinfo-define-common-keys texinfo-mode-map) + + +;;; Variable definitions: + +(require 'shell) + +(defvar texinfo-tex-shell-cd-command "cd" + "Command to give to shell running TeX to change directory.") + +(defvar texinfo-tex-command "tex" + "*Command used by texinfo-tex-region to run tex on a region.") + +(defvar texinfo-texindex-command "texindex" + "*Command used by texinfo-texindex to sort unsorted index files.") + +(defvar texinfo-tex-dvi-print-command "lpr -d" + "*Command string used by \\[tex-print] to print a .dvi file.") + +(defvar texinfo-show-tex-queue-command "lpq" + "*Command string used to show the Texinfo TeX print queue. +Command is used by \\[texinfo-show-tex-print-queue] and it +should show the queue that \\[texinfo-tex-print] puts jobs on.") + +(defvar texinfo-delete-from-print-queue-command "lprm" + "*Command string used to delete a job from the line printer queue. +Command is used by \\[texinfo-delete-from-tex-print-queue] based on +number provided by a previous \\[texinfo-show-tex-print-queue] +command.") + +(defvar texinfo-tex-trailer "@bye" + "String appended after a region sent to TeX by texinfo-tex-region.") + +(defvar texinfo-tex-original-file "" + "Original name of file on which to run TeX.") + +(defvar texinfo-tex-temp-file nil + "Temporary file name used for text being sent as input to TeX.") + +(defvar texinfo-tex-root-temp-file nil + "Temporary file name used for text being sent as input to TeX.") + + +;;; Texinfo TeX main functions + +(defun texinfo-tex-region (beginning end) + "Run tex on the current region. + +A temporary file is written in the default directory, and tex is run +in that directory. The first line of the file is copied to the +temporary file; and if the buffer has a header, it is written to the +temporary file before the region itself. The buffer's header is all +lines between the strings defined by texinfo-start-of-header and +texinfo-end-of-header inclusive. The header must start in the first 100 +lines. The value of texinfo-tex-trailer is appended to the temporary file +after the region." + + (interactive "r") + (if (get-buffer "*texinfo-tex-shell*") + (quit-process (get-process "texinfo-tex-shell") t) + (texinfo-tex-start-shell)) + + (setq texinfo-tex-root-temp-file + (expand-file-name + (make-temp-name + (prin1-to-string (read (buffer-name)))))) + + (let ((texinfo-tex-temp-file (concat texinfo-tex-root-temp-file ".tex"))) + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (forward-line 100) + (let ((search-end (point)) + (header-beginning (point-min)) (header-end (point-min))) + (goto-char (point-min)) + ;; Copy first line, the `\input texinfo' line, to temp file + (write-region (point) + (save-excursion (forward-line 1) (point)) + texinfo-tex-temp-file nil nil) + ;; Don't copy first line twice if region includes it. + (forward-line 1) + (if (< beginning (point)) (setq beginning (point))) + ;; Initialize the temp file with either the header or nothing + (if (search-forward texinfo-start-of-header search-end t) + (progn + (beginning-of-line) + (setq header-beginning (point)) ; Mark beginning of header. + (if (search-forward texinfo-end-of-header nil t) + (progn (beginning-of-line) + (setq header-end (point))) ; Mark end of header. + (setq header-beginning (point-min))))) ; Else no header. + ;; Copy header to temp file. + (write-region + (min header-beginning beginning ) + header-end + texinfo-tex-temp-file t nil) + ;; Copy region to temp file. + (write-region + (max beginning header-end) + end + texinfo-tex-temp-file t nil) + ;; This is a kludge to insert the texinfo-tex-trailer into the + ;; texinfo-tex-temp-file. We have to create a special buffer + ;; in which to insert the texinfo-tex-trailer first because there is + ;; no function with which to append a literal string directly + ;; to a file. + (let ((local-tex-trailer texinfo-tex-trailer) + (temp-buffer (get-buffer-create " texinfo-trailer-buffer"))) + (set-buffer temp-buffer) + (erase-buffer) + ;; make sure trailer isn't hidden by a comment + (insert-string "\n") + (if local-tex-trailer (insert local-tex-trailer)) + (write-region (point-min) (point-max) + texinfo-tex-temp-file t nil))) + (set-process-sentinel (get-process "texinfo-tex-shell") + 'texinfo-tex-shell-sentinel) + (send-string "texinfo-tex-shell" + (concat texinfo-tex-shell-cd-command " " + default-directory "\n")) + (send-string "texinfo-tex-shell" + (concat texinfo-tex-command " " + texinfo-tex-temp-file "\n ")) + (texinfo-recenter-tex-output-buffer 0))))) + +(defun texinfo-tex-buffer (buffer) + "Run TeX on current buffer. +After running TeX the first time, you may have to run \\[texinfo-texindex] +and then \\[texinfo-tex-buffer] again." + (interactive + (list + ;; Sometimes you put point into *texinfo-tex-shell*; this prompts + ;; you for the correct file regardless. + (if (and + (string= (buffer-name (current-buffer)) "*texinfo-tex-shell*") + texinfo-tex-root-temp-file) + (read-string (format "Run TeX on: ") + texinfo-tex-original-file) + (read-string (format "Run TeX on: ") (buffer-name (current-buffer)))))) + + ;; Set to original buffer if in *texinfo-tex-shell*; otherwise, + ;; record name of current buffer. + (if (string= (buffer-name (current-buffer)) "*texinfo-tex-shell*") + (set-buffer buffer) + (setq texinfo-tex-original-file + (buffer-name (current-buffer)))) + + (if (get-buffer "*texinfo-tex-shell*") + (quit-process (get-process "texinfo-tex-shell") t) + (texinfo-tex-start-shell)) + (cond ((null buffer-file-name) + (error "Buffer not visiting any file!")) + ((buffer-modified-p) + (error "Buffer has been modified since last saved!")) + (t (set-process-sentinel (get-process "texinfo-tex-shell") + 'texinfo-tex-shell-sentinel) + (send-string "texinfo-tex-shell" + (concat texinfo-tex-shell-cd-command + " " + (file-name-directory + (buffer-file-name + (get-buffer buffer))) + "\n")) + (send-string "texinfo-tex-shell" + (concat texinfo-tex-command " " buffer "\n ")) + + ;; so the texinfo-tex-print command works + (setq texinfo-tex-root-temp-file + (substring buffer 0 + (or (string-match "\\.tex" buffer) + (length buffer)))) + + (texinfo-recenter-tex-output-buffer 0)))) + +(defun texinfo-texindex () + "Run texindex on unsorted index files. +The index files are made by \\[texinfo-tex-region] or \\[texinfo-tex-buffer]. +Runs the shell command defined by texinfo-texindex-command." + (interactive) + (send-string "texinfo-tex-shell" + (concat texinfo-texindex-command + " " texinfo-tex-root-temp-file ".??" "\n")) + (texinfo-recenter-tex-output-buffer nil)) + +(defun texinfo-tex-print () + "Print .dvi file made by \\[texinfo-tex-region] or \\[texinfo-tex-buffer]. +Runs the shell command defined by texinfo-tex-dvi-print-command." + (interactive) + (send-string "texinfo-tex-shell" + (concat texinfo-tex-dvi-print-command + " " texinfo-tex-root-temp-file ".dvi" "\n")) + (texinfo-recenter-tex-output-buffer nil)) + + +;;; Texinfo TeX utility functions + +(defun texinfo-tex-start-shell () + (save-excursion + (require 'texinfo) + (set-buffer (make-shell "texinfo-tex-shell" "/bin/sh" nil "-v")) + (setq texinfo-tex-shell-map (copy-keymap shell-mode-map)) + (texinfo-define-common-keys texinfo-tex-shell-map) + (use-local-map texinfo-tex-shell-map) + (run-hooks 'texinfo-tex-shell-hook) + (if (zerop (buffer-size)) + (sleep-for 1)))) + +(defun texinfo-quit-tex-job () + "Quit currently running TeX job, by sending an `x' to it." + (interactive) + (if (not (get-process "texinfo-tex-shell")) + (error "No TeX shell running.")) + (save-excursion + (set-buffer (get-buffer "*texinfo-tex-shell*")) + (goto-char (point-max)) + (insert "x") + (shell-send-input))) + +(defun texinfo-kill-tex-job () + "Kill the currently running TeX job." + (interactive) + (if (get-process "texinfo-tex-shell") + ;; Use `texinfo-tex-shell-sentinel' to restart + ;; texinfo-tex-shell after it is killed. + (kill-process (get-process "texinfo-tex-shell")))) + +(defun texinfo-tex-shell-sentinel (process event) + "Restart texinfo-tex-shell after it is killed." + (if (equal event "killed\n") + (save-excursion + (set-buffer "*texinfo-tex-shell*") + (insert "\n") + (texinfo-tex-start-shell)))) + +(defun texinfo-recenter-tex-output-buffer (linenum) + "Redisplay buffer of TeX job output so that most recent output can be seen. +The last line of the buffer is displayed on +line LINE of the window, or centered if LINE is nil." + (interactive "P") + (let ((texinfo-tex-shell (get-buffer "*texinfo-tex-shell*")) + (old-buffer (current-buffer))) + (if (null texinfo-tex-shell) + (message "No TeX output buffer") + (pop-to-buffer texinfo-tex-shell) + (bury-buffer texinfo-tex-shell) + (goto-char (point-max)) + (recenter (if linenum + (prefix-numeric-value linenum) + (/ (window-height) 2))) + (pop-to-buffer old-buffer) + ))) + +(defun texinfo-show-tex-print-queue () + "Show the print queue that \\[texinfo-tex-print] put your job on. +Runs the shell command defined by texinfo-show-tex-queue-command." + (interactive) + (if (not (texinfo-tex-shell-running-p)) + (texinfo-tex-start-shell)) + (send-string "texinfo-tex-shell" + (concat texinfo-show-tex-queue-command "\n")) + (texinfo-recenter-tex-output-buffer nil)) + +(defun texinfo-delete-from-tex-print-queue (job-number) + "Delete job from the line printer spooling queue. +You are prompted for the job number (shown by a previous +\\[texinfo-show-tex-print-queue] command." + (interactive "nPrinter job number for deletion: ") + (if (texinfo-tex-shell-running-p) + (texinfo-kill-tex-job) + (texinfo-tex-start-shell)) + (send-string "texinfo-tex-shell" + (concat + texinfo-delete-from-print-queue-command + " " + job-number"\n")) + (texinfo-recenter-tex-output-buffer nil)) + +(defun texinfo-tex-shell-running-p () + (and (get-process "texinfo-tex-shell") + (eq (process-status (get-process "texinfo-tex-shell")) 'run))) + + +;;; Place `provide' at end of file. +(provide 'texnfo-tex) +;;;;;;;;;;;;;;;; end texnfo-tex.el ;;;;;;;;;;;;;;;; diff --git a/gnu/usr.bin/texinfo/emacs/texnfo-upd.el b/gnu/usr.bin/texinfo/emacs/texnfo-upd.el new file mode 100644 index 00000000000..f84cdd51496 --- /dev/null +++ b/gnu/usr.bin/texinfo/emacs/texnfo-upd.el @@ -0,0 +1,2046 @@ +;;; Texinfo mode utilities for updating nodes and menus in Texinfo files. +;;; Copyright 1989, 1990, 1991, 1992 Free Software Foundation + +;; Author: Robert J. Chassell +;; Maintainer: bug-texinfo@prep.ai.mit.edu +;; Keywords: maint, tex, docs + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +;;; Commentary: + +;;; Known bug: update commands fail to ignore @ignore. + +;;; Summary: how to use the updating commands + +; The node and menu updating functions automatically + +; * insert missing `@node' lines, +; * insert the `Next', `Previous' and `Up' pointers of a node, +; * insert or update the menu for a section, +; * create a master menu for a Texinfo source file. +; +; Passed an argument, the `texinfo-update-node' and +; `texinfo-make-menu' functions do their jobs in the region. +; +; In brief, the functions for creating or updating nodes and menus, are: +; +; texinfo-update-node (&optional region-p) +; texinfo-every-node-update () +; texinfo-sequential-node-update (&optional region-p) +; +; texinfo-make-menu (&optional region-p) +; texinfo-all-menus-update () +; texinfo-master-menu () +; +; texinfo-insert-node-lines (&optional title-p) +; +; texinfo-indent-menu-description (column &optional region-p) + +; The `texinfo-column-for-description' variable specifies the column to +; which menu descriptions are indented. + +; Texinfo file structure +; ---------------------- + +; To use the updating commands, you must structure your Texinfo file +; hierarchically. Each `@node' line, with the exception of the top +; node, must be accompanied by some kind of section line, such as an +; `@chapter' or `@section' line. Each node-line/section-line +; combination must look like this: + +; @node Lists and Tables, Cross References, Structuring, Top +; @comment node-name, next, previous, up +; @chapter Making Lists and Tables + +; or like this (without the `@comment' line): + +; @node Lists and Tables, Cross References, Structuring, Top +; @chapter Making Lists and Tables + +; If the file has a `top' node, it must be called `top' or `Top' and +; be the first node in the file. + + +;;; The update node functions described in detail + +; The `texinfo-update-node' function without an argument inserts +; the correct next, previous and up pointers for the node in which +; point is located (i.e., for the node preceding point). + +; With an argument, the `texinfo-update-node' function inserts the +; correct next, previous and up pointers for the nodes inside the +; region. + +; It does not matter whether the `@node' line has pre-existing +; `Next', `Previous', or `Up' pointers in it. They are removed. + +; The `texinfo-every-node-update' function runs `texinfo-update-node' +; on the whole buffer. + +; The `texinfo-sequential-node-update' function inserts the +; immediately following and preceding node into the `Next' or +; `Previous' pointers regardless of their hierarchical level. This is +; only useful for certain kinds of text, like a novel, which you go +; through sequentially. + + +;;; The menu making functions described in detail + +; The `texinfo-make-menu' function without an argument creates or +; updates a menu for the section encompassing the node that follows +; point. With an argument, it makes or updates menus for the nodes +; within or part of the marked region. + +; Whenever an existing menu is updated, the descriptions from +; that menu are incorporated into the new menu. This is done by copying +; descriptions from the existing menu to the entries in the new menu +; that have the same node names. If the node names are different, the +; descriptions are not copied to the new menu. + +; Menu entries that refer to other Info files are removed since they +; are not a node within current buffer. This is a deficiency. + +; The `texinfo-all-menus-update' function runs `texinfo-make-menu' +; on the whole buffer. + +; The `texinfo-master-menu' function creates an extended menu located +; after the top node. (The file must have a top node.) The function +; first updates all the regular menus in the buffer (incorporating the +; descriptions from pre-existing menus), and then constructs a master +; menu that includes every entry from every other menu. (However, the +; function cannot update an already existing master menu; if one +; exists, it must be removed before calling the function.) + +; The `texinfo-indent-menu-description' function indents every +; description in the menu following point, to the specified column. +; Non-nil argument (prefix, if interactive) means indent every +; description in every menu in the region. This function does not +; indent second and subsequent lines of a multi-line description. + +; The `texinfo-insert-node-lines' function inserts `@node' before the +; `@chapter', `@section', and such like lines of a region in a Texinfo +; file where the `@node' lines are missing. +; +; With a non-nil argument (prefix, if interactive), the function not +; only inserts `@node' lines but also inserts the chapter or section +; titles as the names of the corresponding nodes; and inserts titles +; as node names in pre-existing `@node' lines that lack names. +; +; Since node names should be more concise than section or chapter +; titles, node names so inserted will need to be edited manually. + + +;;; Code: + +;;; The menu making functions + +(defun texinfo-make-menu (&optional region-p) + "Without any prefix argument, make or update a menu. +Make the menu for the section enclosing the node found following point. + +Non-nil argument (prefix, if interactive) means make or update menus +for nodes within or part of the marked region. + +Whenever a menu exists, and is being updated, the descriptions that +are associated with node names in the pre-existing menu are +incorporated into the new menu. Otherwise, the nodes' section titles +are inserted as descriptions." + + (interactive "P") + (if (not region-p) + (let ((level (texinfo-hierarchic-level))) + (texinfo-make-one-menu level) + (message "Done...updated the menu. You may save the buffer.")) + ;; else + (message "Making or updating menus in %s... " (buffer-name)) + (let ((beginning (region-beginning)) + (region-end (region-end)) + (level (progn ; find section type following point + (goto-char (region-beginning)) + (texinfo-hierarchic-level)))) + (if (= region-end beginning) + (error "Please mark a region!")) + (save-excursion + (save-restriction + (widen) + + (while (texinfo-find-lower-level-node level region-end) + (setq level (texinfo-hierarchic-level)) ; new, lower level + (texinfo-make-one-menu level)) + + (while (and (< (point) region-end) + (texinfo-find-higher-level-node level region-end)) + (setq level (texinfo-hierarchic-level)) + (while (texinfo-find-lower-level-node level region-end) + (setq level (texinfo-hierarchic-level)) ; new, lower level + (texinfo-make-one-menu level)))))) + (message "Done...updated menus. You may save the buffer."))) + +(defun texinfo-make-one-menu (level) + "Make a menu of all the appropriate nodes in this section. +`Appropriate nodes' are those associated with sections that are +at the level specified by LEVEL. Point is left at the end of menu." + (let* + ((case-fold-search t) + (beginning + (save-excursion + (goto-char (texinfo-update-menu-region-beginning level)) + (end-of-line) + (point))) + (end (texinfo-update-menu-region-end level)) + (first (texinfo-menu-first-node beginning end)) + (node-name (progn + (goto-char beginning) + (beginning-of-line) + (texinfo-copy-node-name))) + (new-menu-list (texinfo-make-menu-list beginning end level))) + (if (texinfo-old-menu-p beginning first) + (progn + (texinfo-incorporate-descriptions new-menu-list) + (texinfo-incorporate-menu-entry-names new-menu-list) + (texinfo-delete-old-menu beginning first))) + (texinfo-insert-menu new-menu-list node-name))) + +(defun texinfo-all-menus-update (&optional update-all-nodes-p) + "Update every regular menu in a Texinfo file. +Update pre-existing master menu, if there is one. + +If called with a non-nil argument, this function first updates all the +nodes in the buffer before updating the menus." + (interactive "P") + (let ((case-fold-search t) + master-menu-p) + (save-excursion + (push-mark (point-max) t) + (goto-char (point-min)) + (message "Checking for a master menu in %s ... "(buffer-name)) + (save-excursion + (if (re-search-forward texinfo-master-menu-header nil t) + ;; Remove detailed master menu listing + (progn + (setq master-menu-p t) + (goto-char (match-beginning 0)) + (let ((end-of-detailed-menu-descriptions + (save-excursion ; beginning of end menu line + (goto-char (texinfo-menu-end)) + (beginning-of-line) (forward-char -1) + (point)))) + (delete-region (point) end-of-detailed-menu-descriptions))))) + + (if update-all-nodes-p + (progn + (message "Updating all nodes in %s ... " (buffer-name)) + (sleep-for 2) + (push-mark (point-max) t) + (goto-char (point-min)) + ;; Using the mark to pass bounds this way + ;; is kludgy, but it's not worth fixing. -- rms. + (let ((mark-active t)) + (texinfo-update-node t)))) + + (message "Updating all menus in %s ... " (buffer-name)) + (sleep-for 2) + (push-mark (point-max) t) + (goto-char (point-min)) + ;; Using the mark to pass bounds this way + ;; is kludgy, but it's not worth fixing. -- rms. + (let ((mark-active t)) + (texinfo-make-menu t)) + + (if master-menu-p + (progn + (message "Updating the master menu in %s... " (buffer-name)) + (sleep-for 2) + (texinfo-master-menu nil)))) + + (message "Done...updated all the menus. You may save the buffer."))) + +(defun texinfo-find-lower-level-node (level region-end) + "Search forward from point for node at any level lower than LEVEL. +Search is limited to the end of the marked region, REGION-END, +and to the end of the menu region for the level. + +Return t if the node is found, else nil. Leave point at the beginning +of the node if one is found; else do not move point." + (let ((case-fold-search t)) + (if (and (< (point) region-end) + (re-search-forward + (concat + "\\(^@node\\).*\n" ; match node line + "\\(\\(\\(^@c\\).*\n\\)" ; match comment line, if any + "\\|" ; or + "\\(^@ifinfo[ ]*\n\\)\\)?" ; ifinfo line, if any + (eval (cdr (assoc level texinfo-update-menu-lower-regexps)))) + ;; the next higher level node marks the end of this + ;; section, and no lower level node will be found beyond + ;; this position even if region-end is farther off + (texinfo-update-menu-region-end level) + t)) + (goto-char (match-beginning 1))))) + +(defun texinfo-find-higher-level-node (level region-end) + "Search forward from point for node at any higher level than argument LEVEL. +Search is limited to the end of the marked region, REGION-END. + +Return t if the node is found, else nil. Leave point at the beginning +of the node if one is found; else do not move point." + (let ((case-fold-search t)) + (cond + ((or (string-equal "top" level) (string-equal "chapter" level)) + (if (re-search-forward "^@node [ \t]*top[ \t]*\\(,\\|$\\)" region-end t) + (progn (beginning-of-line) t))) + (t + (if (re-search-forward + (concat + "\\(^@node\\).*\n" ; match node line + "\\(\\(\\(^@c\\).*\n\\)" ; match comment line, if any + "\\|" ; or + "\\(^@ifinfo[ ]*\n\\)\\)?" ; ifinfo line, if any + (eval (cdr (assoc level texinfo-update-menu-higher-regexps)))) + region-end t) + (progn (beginning-of-line) t)))))) + + +;;; Making the list of new menu entries + +(defun texinfo-make-menu-list (beginning end level) + "Make a list of node names and their descriptions. +Point is left at the end of the menu region, but the menu is not inserted. + +First argument is position from which to start making menu list; +second argument is end of region in which to try to locate entries; +third argument is the level of the nodes that are the entries. + +Node names and descriptions are dotted pairs of strings. Each pair is +an element of the list. If the description does not exist, the +element consists only of the node name." + (goto-char beginning) + (let (new-menu-list) + (while (texinfo-menu-locate-entry-p level end) + (setq new-menu-list + (cons (cons + (texinfo-copy-node-name) + (prog1 "" (forward-line 1))) + ;; Use following to insert section titles automatically. + ;; (texinfo-copy-section-title)) + new-menu-list))) + (reverse new-menu-list))) + +(defun texinfo-menu-locate-entry-p (level search-end) + "Find a node that will be part of menu for this section. +First argument is a string such as \"section\" specifying the general +hierarchical level of the menu; second argument is a position +specifying the end of the search. + +The function returns t if the node is found, else nil. It searches +forward from point, and leaves point at the beginning of the node. + +The function finds entries of the same type. Thus `subsections' and +`unnumberedsubsecs' will appear in the same menu." + (let ((case-fold-search t)) + (if (re-search-forward + (concat + "\\(^@node\\).*\n" ; match node line + "\\(\\(\\(^@c\\).*\n\\)" ; match comment line, if any + "\\|" ; or + "\\(^@ifinfo[ ]*\n\\)\\)?" ; ifinfo line, if any + (eval + (cdr (assoc level texinfo-update-menu-same-level-regexps)))) + search-end + t) + (goto-char (match-beginning 1))))) + +(defun texinfo-copy-node-name () + "Return the node name as a string. + +Start with point at the beginning of the node line; copy the text +after the node command up to the first comma on the line, if any, and +return the text as a string. Leaves point at the beginning of the +line. If there is no node name, returns an empty string." + + (save-excursion + (buffer-substring + (progn (forward-word 1) ; skip over node command + (skip-chars-forward " \t") ; and over spaces + (point)) + (if (search-forward + "," + (save-excursion (end-of-line) (point)) t) ; bound search + (1- (point)) + (end-of-line) (point))))) + +(defun texinfo-copy-section-title () + "Return the title of the section as a string. +The title is used as a description line in the menu when one does not +already exist. + +Move point to the beginning of the appropriate section line by going +to the start of the text matched by last regexp searched for, which +must have been done by `texinfo-menu-locate-entry-p'." + + ;; could use the same re-search as in `texinfo-menu-locate-entry-p' + ;; instead of using `match-beginning'; such a variation would be + ;; more general, but would waste information already collected + + (goto-char (match-beginning 7)) ; match section name + + (buffer-substring + (progn (forward-word 1) ; skip over section type + (skip-chars-forward " \t") ; and over spaces + (point)) + (progn (end-of-line) (point)))) + + +;;; Handling the old menu + +(defun texinfo-old-menu-p (beginning first) + "Move point to the beginning of the menu for this section, if any. +Otherwise move point to the end of the first node of this section. +Return t if a menu is found, nil otherwise. + +First argument is the position of the beginning of the section in which +the menu will be located; second argument is the position of the first +node within the section. + +If no menu is found, the function inserts two newlines just before the +end of the section, and leaves point there where a menu ought to be." + (goto-char beginning) + (if (not (re-search-forward "^@menu" first 'goto-end)) + (progn (insert "\n\n") (forward-line -2) nil) + t)) + +(defun texinfo-incorporate-descriptions (new-menu-list) + "Copy the old menu line descriptions that exist to the new menu. + +Point must be at beginning of old menu. + +If the node-name of the new menu is found in the old menu, insert the +old description into the new entry. + +For this function, the new menu is a list made up of lists of dotted +pairs in which the first element of the pair is the node name and the +second element the description. The new menu is changed destructively. +The old menu is the menu as it appears in the texinfo file." + + (let ((new-menu-list-pointer new-menu-list) + (end-of-menu (texinfo-menu-end))) + (while new-menu-list + (save-excursion ; keep point at beginning of menu + (if (re-search-forward + ;; Existing nodes can have the form + ;; * NODE NAME:: DESCRIPTION + ;; or + ;; * MENU ITEM: NODE NAME. DESCRIPTION. + ;; + ;; Recognize both when looking for the description. + (concat "\\* \\(" ; so only menu entries are found + (car (car new-menu-list)) "::" + "\\|" + ".*: " (car (car new-menu-list)) "[.,\t\n]" + "\\)" + ) ; so only complete entries are found + end-of-menu + t) + (setcdr (car new-menu-list) + (texinfo-menu-copy-old-description end-of-menu)))) + (setq new-menu-list (cdr new-menu-list))) + (setq new-menu-list new-menu-list-pointer))) + +(defun texinfo-incorporate-menu-entry-names (new-menu-list) + "Copy any old menu entry names to the new menu. + +Point must be at beginning of old menu. + +If the node-name of the new menu entry cannot be found in the old +menu, do nothing. + +For this function, the new menu is a list made up of lists of dotted +pairs in which the first element of the pair is the node name and the +second element is the description (or nil). + +If we find an existing menu entry name, we change the first element of +the pair to be another dotted pair in which the car is the menu entry +name and the cdr is the node name. + +NEW-MENU-LIST is changed destructively. The old menu is the menu as it +appears in the texinfo file." + + (let ((new-menu-list-pointer new-menu-list) + (end-of-menu (texinfo-menu-end))) + (while new-menu-list + (save-excursion ; keep point at beginning of menu + (if (re-search-forward + ;; Existing nodes can have the form + ;; * NODE NAME:: DESCRIPTION + ;; or + ;; * MENU ITEM: NODE NAME. DESCRIPTION. + ;; + ;; We're interested in the second case. + (concat "\\* " ; so only menu entries are found + "\\(.*\\): " (car (car new-menu-list)) "[.,\t\n]") + end-of-menu + t) + (setcar + (car new-menu-list) ; replace the node name + (cons (buffer-substring (match-beginning 1) (match-end 1)) + (car (car new-menu-list))))) + (setq new-menu-list (cdr new-menu-list)))) + (setq new-menu-list new-menu-list-pointer))) + +(defun texinfo-menu-copy-old-description (end-of-menu) + "Return description field of old menu line as string. +Point must be located just after the node name. Point left before description. +Single argument, END-OF-MENU, is position limiting search." + (skip-chars-forward "[:.,\t\n ]+") + ;; don't copy a carriage return at line beginning with asterisk! + ;; do copy a description that begins with an `@'! + ;; !! Known bug: does not copy descriptions starting with ^|\{?* etc. + (if (and (looking-at "\\(\\w+\\|@\\)") + (not (looking-at "\\(^\\* \\|^@end menu\\)"))) + (buffer-substring + (point) + (save-excursion + (re-search-forward "\\(^\\* \\|^@end menu\\)" end-of-menu t) + (forward-line -1) + (end-of-line) ; go to end of last description line + (point))) + "")) + +(defun texinfo-menu-end () + "Return position of end of menu. Does not change location of point. +Signal an error if not end of menu." + (save-excursion + (if (re-search-forward "^@end menu" nil t) + (point) + (error "Menu does not have an end.")))) + +(defun texinfo-delete-old-menu (beginning first) + "Delete the old menu. Point must be in or after menu. +First argument is position of the beginning of the section in which +the menu will be located; second argument is the position of the first +node within the section." + ;; No third arg to search, so error if search fails. + (re-search-backward "^@menu" beginning) + (delete-region (point) + (save-excursion + (re-search-forward "^@end menu" first) + (point)))) + + +;;; Inserting new menu + +;; try 32, but perhaps 24 is better +(defvar texinfo-column-for-description 32 + "*Column at which descriptions start in a Texinfo menu.") + +(defun texinfo-insert-menu (menu-list node-name) + "Insert formatted menu at point. +Indents the first line of the description, if any, to the value of +texinfo-column-for-description. + +MENU-LIST has form: + + \(\(\"node-name1\" . \"description\"\) + \(\"node-name2\" . \"description\"\) ... \) + +However, the description field might be nil. + +Also, the node-name field might itself be a dotted pair (call it P) of +strings instead of just a string. In that case, the car of P +is the menu entry name, and the cdr of P is the node name." + + (insert "@menu\n") + (while menu-list + ;; Every menu entry starts with a star and a space. + (insert "* ") + + ;; Insert the node name (and menu entry name, if present). + (let ((node-part (car (car menu-list)))) + (if (stringp node-part) + ;; "Double colon" entry line; menu entry and node name are the same, + (insert (format "%s::" node-part)) + ;; "Single colon" entry line; menu entry and node name are different. + (insert (format "%s: %s." (car node-part) (cdr node-part))))) + + ;; Insert the description, if present. + (if (cdr (car menu-list)) + (progn + ;; Move to right place. + (indent-to texinfo-column-for-description 2) + ;; Insert description. + (insert (format "%s" (cdr (car menu-list)))))) + + (insert "\n") ; end this menu entry + (setq menu-list (cdr menu-list))) + (insert "@end menu") + (message + "Updated \"%s\" level menu following node: %s ... " level node-name)) + + +;;; Starting menu descriptions by inserting titles + +(defun texinfo-start-menu-description () + "In this menu entry, insert the node's section title as a description. +Position point at beginning of description ready for editing. +Do not insert a title if the line contains an existing description. + +You will need to edit the inserted text since a useful description +complements the node name rather than repeats it as a title does." + + (interactive) + (let (beginning end node-name title) + (save-excursion + (beginning-of-line) + (if (search-forward "* " (save-excursion (end-of-line) (point)) t) + (progn (skip-chars-forward " \t") + (setq beginning (point))) + (error "This is not a line in a menu!")) + + (cond + ;; "Double colon" entry line; menu entry and node name are the same, + ((search-forward "::" (save-excursion (end-of-line) (point)) t) + (if (looking-at "[ \t]*[^ \t\n]+") + (error "Descriptive text already exists.")) + (skip-chars-backward ": \t") + (setq node-name (buffer-substring beginning (point)))) + + ;; "Single colon" entry line; menu entry and node name are different. + ((search-forward ":" (save-excursion (end-of-line) (point)) t) + (skip-chars-forward " \t") + (setq beginning (point)) + ;; Menu entry line ends in a period, comma, or tab. + (if (re-search-forward "[.,\t]" + (save-excursion (forward-line 1) (point)) t) + (progn + (if (looking-at "[ \t]*[^ \t\n]+") + (error "Descriptive text already exists.")) + (skip-chars-backward "., \t") + (setq node-name (buffer-substring beginning (point)))) + ;; Menu entry line ends in a return. + (re-search-forward ".*\n" + (save-excursion (forward-line 1) (point)) t) + (skip-chars-backward " \t\n") + (setq node-name (buffer-substring beginning (point))) + (if (= 0 (length node-name)) + (error "No node name on this line.") + (insert ".")))) + (t (error "No node name on this line."))) + ;; Search for node that matches node name, and copy the section title. + (if (re-search-forward + (concat + "^@node[ \t]+" + node-name + ".*\n" ; match node line + "\\(" + "\\(\\(^@c \\|^@comment\\).*\n\\)" ; match comment line, if any + "\\|" ; or + "\\(^@ifinfo[ ]*\n\\)" ; ifinfo line, if any + "\\)?") + nil t) + (progn + (setq title + (buffer-substring + ;; skip over section type + (progn (forward-word 1) + ;; and over spaces + (skip-chars-forward " \t") + (point)) + (progn (end-of-line) + (skip-chars-backward " \t") + (point))))) + (error "Cannot find node to match node name in menu entry."))) + ;; Return point to the menu and insert the title. + (end-of-line) + (delete-region + (point) + (save-excursion (skip-chars-backward " \t") (point))) + (indent-to texinfo-column-for-description 2) + (save-excursion (insert title)))) + + +;;; Handling description indentation + +; Since the make-menu functions indent descriptions, these functions +; are useful primarily for indenting a single menu specially. + +(defun texinfo-indent-menu-description (column &optional region-p) + "Indent every description in menu following point to COLUMN. +Non-nil argument (prefix, if interactive) means indent every +description in every menu in the region. Does not indent second and +subsequent lines of a multi-line description." + + (interactive + "nIndent menu descriptions to (column number): \nP") + (save-excursion + (save-restriction + (widen) + (if (not region-p) + (progn + (re-search-forward "^@menu") + (texinfo-menu-indent-description column) + (message + "Indented descriptions in menu. You may save the buffer.")) + ;;else + (message "Indenting every menu description in region... ") + (goto-char (region-beginning)) + (while (and (< (point) (region-end)) + (texinfo-locate-menu-p)) + (forward-line 1) + (texinfo-menu-indent-description column)) + (message "Indenting done. You may save the buffer."))))) + +(defun texinfo-menu-indent-description (to-column-number) + "Indent the Texinfo file menu description to TO-COLUMN-NUMBER. +Start with point just after the word `menu' in the `@menu' line and +leave point on the line before the `@end menu' line. Does not indent +second and subsequent lines of a multi-line description." + (let* ((beginning-of-next-line (point))) + (while (< beginning-of-next-line + (save-excursion ; beginning of end menu line + (goto-char (texinfo-menu-end)) + (beginning-of-line) + (point))) + + (if (re-search-forward "\\* \\(.*::\\|.*: [^.,\t\n]+[.,\t]\\)" + (texinfo-menu-end) + t) + (progn + (let ((beginning-white-space (point))) + (skip-chars-forward " \t") ; skip over spaces + (if (looking-at "\\(@\\|\\w\\)+") ; if there is text + (progn + ;; remove pre-existing indentation + (delete-region beginning-white-space (point)) + (indent-to-column to-column-number)))))) + ;; position point at beginning of next line + (forward-line 1) + (setq beginning-of-next-line (point))))) + + +;;; Making the master menu + +(defun texinfo-master-menu (update-all-nodes-menus-p) + "Make a master menu for a whole Texinfo file. +Non-nil argument (prefix, if interactive) means first update all +existing nodes and menus. Remove pre-existing master menu, if there is one. + +This function creates a master menu that follows the top node. The +master menu includes every entry from all the other menus. It +replaces any existing ordinary menu that follows the top node. + +If called with a non-nil argument, this function first updates all the +menus in the buffer (incorporating descriptions from pre-existing +menus) before it constructs the master menu. + +The function removes the detailed part of an already existing master +menu. This action depends on the pre-exisitng master menu using the +standard `texinfo-master-menu-header'. + +The master menu has the following format, which is adapted from the +recommendation in the Texinfo Manual: + + * The first part contains the major nodes in the Texinfo file: the + nodes for the chapters, chapter-like sections, and the major + appendices. This includes the indices, so long as they are in + chapter-like sections, such as unnumbered sections. + + * The second and subsequent parts contain a listing of the other, + lower level menus, in order. This way, an inquirer can go + directly to a particular node if he or she is searching for + specific information. + +Each of the menus in the detailed node listing is introduced by the +title of the section containing the menu." + + (interactive "P") + (let ((case-fold-search t)) + (widen) + (goto-char (point-min)) + + ;; Move point to location after `top'. + (if (not (re-search-forward "^@node [ \t]*top[ \t]*\\(,\\|$\\)" nil t)) + (error "This buffer needs a Top node!")) + + (let ((first-chapter + (save-excursion + (or (re-search-forward "^@node" nil t) + (error "Too few nodes for a master menu!")) + (point)))) + (if (re-search-forward texinfo-master-menu-header first-chapter t) + ;; Remove detailed master menu listing + (progn + (goto-char (match-beginning 0)) + (let ((end-of-detailed-menu-descriptions + (save-excursion ; beginning of end menu line + (goto-char (texinfo-menu-end)) + (beginning-of-line) (forward-char -1) + (point)))) + (delete-region (point) end-of-detailed-menu-descriptions))))) + + (if update-all-nodes-menus-p + (progn + (message "Making a master menu in %s ...first updating all nodes... " + (buffer-name)) + (sleep-for 2) + (push-mark (point-max) t) + (goto-char (point-min)) + (texinfo-update-node t) + + (message "Updating all menus in %s ... " (buffer-name)) + (sleep-for 2) + (push-mark (point-max) t) + (goto-char (point-min)) + (texinfo-make-menu t))) + + (message "Now making the master menu in %s... " (buffer-name)) + (sleep-for 2) + (goto-char (point-min)) + (texinfo-insert-master-menu-list + (texinfo-master-menu-list)) + + ;; Remove extra newlines that texinfo-insert-master-menu-list + ;; may have inserted. + + (save-excursion + (goto-char (point-min)) + + (if (re-search-forward texinfo-master-menu-header nil t) + (progn + (goto-char (match-beginning 0)) + (insert "\n") + (delete-blank-lines) + (goto-char (point-min)))) + + (re-search-forward "^@menu") + (forward-line -1) + (delete-blank-lines) + + (re-search-forward "^@end menu") + (forward-line 1) + (delete-blank-lines)) + + (message + "Done...completed making master menu. You may save the buffer."))) + +(defun texinfo-master-menu-list () + "Return a list of menu entries and header lines for the master menu. + +Start with the menu for chapters and indices and then find each +following menu and the title of the node preceding that menu. + +The master menu list has this form: + + \(\(\(... \"entry-1-2\" \"entry-1\"\) \"title-1\"\) + \(\(... \"entry-2-2\" \"entry-2-1\"\) \"title-2\"\) + ...\) + +However, there does not need to be a title field." + + (let (master-menu-list) + (while (texinfo-locate-menu-p) + (setq master-menu-list + (cons (list + (texinfo-copy-menu) + (texinfo-copy-menu-title)) + master-menu-list))) + (reverse master-menu-list))) + +(defun texinfo-insert-master-menu-list (master-menu-list) + "Format and insert the master menu in the current buffer." + (goto-char (point-min)) + ;; Insert a master menu only after `Top' node and before next node + ;; \(or include file if there is no next node\). + (if (not (re-search-forward "^@node [ \t]*top[ \t]*\\(,\\|$\\)" nil t)) + (error "This buffer needs a Top node!")) + (let ((first-chapter + (save-excursion (re-search-forward "^@node\\|^@include") (point)))) + (if (not (re-search-forward "^@menu" first-chapter t)) + (error + "Buffer lacks ordinary `Top' menu in which to insert master."))) + (beginning-of-line) + (delete-region ; buffer must have ordinary top menu + (point) + (save-excursion (re-search-forward "^@end menu") (point))) + + (save-excursion ; leave point at beginning of menu + ;; Handle top of menu + (insert "\n@menu\n") + ;; Insert chapter menu entries + (setq this-very-menu-list (reverse (car (car master-menu-list)))) + ;; Tell user what is going on. + (message "Inserting chapter menu entry: %s ... " this-very-menu-list) + (while this-very-menu-list + (insert "* " (car this-very-menu-list) "\n") + (setq this-very-menu-list (cdr this-very-menu-list))) + + (setq master-menu-list (cdr master-menu-list)) + + ;; Only insert detailed master menu if there is one.... + (if (car (car master-menu-list)) + (insert texinfo-master-menu-header)) + + ;; Now, insert all the other menus + + ;; The menu master-menu-list has a form like this: + ;; ((("beta" "alpha") "title-A") + ;; (("delta" "gamma") "title-B")) + + (while master-menu-list + + (message + "Inserting menu for %s .... " (car (cdr (car master-menu-list)))) + ;; insert title of menu section + (insert "\n" (car (cdr (car master-menu-list))) "\n\n") + + ;; insert each menu entry + (setq this-very-menu-list (reverse (car (car master-menu-list)))) + (while this-very-menu-list + (insert "* " (car this-very-menu-list) "\n") + (setq this-very-menu-list (cdr this-very-menu-list))) + + (setq master-menu-list (cdr master-menu-list))) + + ;; Finish menu + (insert "@end menu\n\n"))) + +(defvar texinfo-master-menu-header + "\n --- The Detailed Node Listing ---\n" + "String inserted before lower level entries in Texinfo master menu. +It comes after the chapter-level menu entries.") + +(defun texinfo-locate-menu-p () + "Find the next menu in the texinfo file. +If found, leave point after word `menu' on the `@menu' line, and return t. +If a menu is not found, do not move point and return nil." + (re-search-forward "\\(^@menu\\)" nil t)) + +(defun texinfo-copy-menu-title () + "Return the title of the section preceding the menu as a string. +If such a title cannot be found, return an empty string. Do not move +point." + (let ((case-fold-search t)) + (save-excursion + (if (re-search-backward + (concat + "\\(^@top" + "\\|" ; or + texinfo-section-types-regexp ; all other section types + "\\)") + nil + t) + (progn + (beginning-of-line) + (forward-word 1) ; skip over section type + (skip-chars-forward " \t") ; and over spaces + (buffer-substring + (point) + (progn (end-of-line) (point)))) + "")))) + +(defun texinfo-copy-menu () + "Return the entries of an existing menu as a list. +Start with point just after the word `menu' in the `@menu' line +and leave point on the line before the `@end menu' line." + (let* (this-menu-list + (end-of-menu (texinfo-menu-end)) ; position of end of `@end menu' + (last-entry (save-excursion ; position of beginning of + ; last `* ' entry + (goto-char end-of-menu) + ;; handle multi-line description + (if (not (re-search-backward "^\* " nil t)) + (error "No entries in menu.")) + (point)))) + (while (< (point) last-entry) + (if (re-search-forward "^\* " end-of-menu t) + (progn + (setq this-menu-list + (cons + (buffer-substring + (point) + ;; copy multi-line descriptions + (save-excursion + (re-search-forward "\\(^\* \\|^@e\\)" nil t) + (- (point) 3))) + this-menu-list))))) + this-menu-list)) + + +;;; Determining the hierarchical level in the texinfo file + +(defun texinfo-specific-section-type () + "Return the specific type of next section, as a string. +For example, \"unnumberedsubsec\". Return \"top\" for top node. + +Searches forward for a section. Hence, point must be before the +section whose type will be found. Does not move point. Signal an +error if the node is not the top node and a section is not found." + (let ((case-fold-search t)) + (save-excursion + (cond + ((re-search-forward "^@node [ \t]*top[ \t]*\\(,\\|$\\)" +;;; Following search limit by cph but causes a bug +;;; (save-excursion +;;; (end-of-line) +;;; (point)) + nil + t) + "top") + ((re-search-forward texinfo-section-types-regexp nil t) + (buffer-substring (progn (beginning-of-line) ; copy its name + (1+ (point))) + (progn (forward-word 1) + (point)))) + (t + (error + "texinfo-specific-section-type: Chapter or section not found.")))))) + +(defun texinfo-hierarchic-level () + "Return the general hierarchal level of the next node in a texinfo file. +Thus, a subheading or appendixsubsec is of type subsection." + (let ((case-fold-search t)) + (cdr (assoc + (texinfo-specific-section-type) + texinfo-section-to-generic-alist)))) + + +;;; Locating the major positions + +(defun texinfo-update-menu-region-beginning (level) + "Locate beginning of higher level section this section is within. +Return position of the beginning of the node line; do not move point. +Thus, if this level is subsection, searches backwards for section node. +Only argument is a string of the general type of section." + (let ((case-fold-search t)) + ;; !! Known bug: if section immediately follows top node, this + ;; returns the beginning of the buffer as the beginning of the + ;; higher level section. + (cond + ((or (string-equal "top" level) + (string-equal "chapter" level)) + (save-excursion + (goto-char (point-min)) + (re-search-forward "^@node [ \t]*top[ \t]*\\(,\\|$\\)" nil t) + (beginning-of-line) + (point))) + (t + (save-excursion + (re-search-backward + (concat + "\\(^@node\\).*\n" ; match node line + "\\(\\(\\(^@c\\).*\n\\)" ; match comment line, if any + "\\|" ; or + "\\(^@ifinfo[ ]*\n\\)\\)?" ; ifinfo line, if any + (eval + (cdr (assoc level texinfo-update-menu-higher-regexps)))) + nil + 'goto-beginning) + (point)))))) + +(defun texinfo-update-menu-region-end (level) + "Locate end of higher level section this section is within. +Return position; do not move point. Thus, if this level is a +subsection, find the node for the section this subsection is within. +If level is top or chapter, returns end of file. Only argument is a +string of the general type of section." + (let ((case-fold-search t)) + (save-excursion + (if (re-search-forward + (concat + "\\(^@node\\).*\n" ; match node line + "\\(\\(\\(^@c\\).*\n\\)" ; match comment line, if any + "\\|" ; or + "\\(^@ifinfo[ ]*\n\\)\\)?" ; ifinfo line, if any + (eval + ;; Never finds end of level above chapter so goes to end. + (cdr (assoc level texinfo-update-menu-higher-regexps)))) + nil + 'goto-end) + (match-beginning 1) + (point-max))))) + +(defun texinfo-menu-first-node (beginning end) + "Locate first node of the section the menu will be placed in. +Return position; do not move point. +The menu will be located just before this position. + +First argument is the position of the beginning of the section in +which the menu will be located; second argument is the position of the +end of that region; it limits the search." + + (save-excursion + (goto-char beginning) + (forward-line 1) + (re-search-forward "^@node" end t) + (beginning-of-line) + (point))) + + +;;; Alists and regular expressions for defining hierarchical levels + +(defvar texinfo-section-to-generic-alist + '(("top" . "top") + + ("chapter" . "chapter") + ("unnumbered" . "chapter") + ("majorheading" . "chapter") + ("chapheading" . "chapter") + ("appendix" . "chapter") + + ("section" . "section") + ("unnumberedsec" . "section") + ("heading" . "section") + ("appendixsec" . "section") + + ("subsection" . "subsection") + ("unnumberedsubsec" . "subsection") + ("subheading" . "subsection") + ("appendixsubsec" . "subsection") + + ("subsubsection" . "subsubsection") + ("unnumberedsubsubsec" . "subsubsection") + ("subsubheading" . "subsubsection") + ("appendixsubsubsec" . "subsubsection")) + "*An alist of specific and corresponding generic Texinfo section types. +The keys are strings specifying specific types of section; the values +are strings of their corresponding general types.") + +;; We used to look for just sub, but that found @subtitle. +(defvar texinfo-section-types-regexp + "^@\\(chapter \\|sect\\|subs\\|subh\\|unnum\\|major\\|chapheading \\|heading \\|appendix\\)" + "Regexp matching chapter, section, other headings (but not the top node).") + +(defvar texinfo-chapter-level-regexp + "chapter\\|unnumbered \\|appendix \\|majorheading\\|chapheading" + "Regular expression matching just the Texinfo chapter level headings.") + +(defvar texinfo-section-level-regexp + "section\\|unnumberedsec\\|heading \\|appendixsec" + "Regular expression matching just the Texinfo section level headings.") + +(defvar texinfo-subsection-level-regexp + "subsection\\|unnumberedsubsec\\|subheading\\|appendixsubsec" + "Regular expression matching just the Texinfo subsection level headings.") + +(defvar texinfo-subsubsection-level-regexp + "subsubsection\\|unnumberedsubsubsec\\|subsubheading\\|appendixsubsubsec" + "Regular expression matching just the Texinfo subsubsection level headings.") + +(defvar texinfo-update-menu-same-level-regexps + '(("top" . "top[ \t]+") + ("chapter" . + (concat "\\(^@\\)\\(" texinfo-chapter-level-regexp "\\)[ \t]*")) + ("section" . + (concat "\\(^@\\)\\(" texinfo-section-level-regexp "\\)[ \t]*")) + ("subsection" . + (concat "\\(^@\\)\\(" texinfo-subsection-level-regexp "\\)[ \t]+")) + ("subsubsection" . + (concat "\\(^@\\)\\(" texinfo-subsubsection-level-regexp "\\)[ \t]+"))) + "*Regexps for searching for same level sections in a Texinfo file. +The keys are strings specifying the general hierarchical level in the +document; the values are regular expressions.") + +(defvar texinfo-update-menu-higher-regexps + '(("top" . "^@node [ \t]*DIR") + ("chapter" . "^@node [ \t]*top[ \t]*\\(,\\|$\\)") + ("section" . + (concat + "\\(^@\\(" + texinfo-chapter-level-regexp + "\\)[ \t]*\\)")) + ("subsection" . + (concat + "\\(^@\\(" + texinfo-section-level-regexp + "\\|" + texinfo-chapter-level-regexp + "\\)[ \t]*\\)")) + ("subsubsection" . + (concat + "\\(^@\\(" + texinfo-subsection-level-regexp + "\\|" + texinfo-section-level-regexp + "\\|" + texinfo-chapter-level-regexp + "\\)[ \t]*\\)"))) + "*Regexps for searching for higher level sections in a Texinfo file. +The keys are strings specifying the general hierarchical level in the +document; the values are regular expressions.") + +(defvar texinfo-update-menu-lower-regexps + '(("top" . + (concat + "\\(^@\\(" + texinfo-chapter-level-regexp + "\\|" + texinfo-section-level-regexp + "\\|" + texinfo-subsection-level-regexp + "\\|" + texinfo-subsubsection-level-regexp + "\\)[ \t]*\\)")) + ("chapter" . + (concat + "\\(^@\\(" + texinfo-section-level-regexp + "\\|" + texinfo-subsection-level-regexp + "\\|" + texinfo-subsubsection-level-regexp + "\\)[ \t]*\\)")) + ("section" . + (concat + "\\(^@\\(" + texinfo-subsection-level-regexp + "\\|" + texinfo-subsubsection-level-regexp + "\\)[ \t]+\\)")) + ("subsection" . + (concat + "\\(^@\\(" + texinfo-subsubsection-level-regexp + "\\)[ \t]+\\)")) + ("subsubsection" . "nothing lower")) + "*Regexps for searching for lower level sections in a Texinfo file. +The keys are strings specifying the general hierarchical level in the +document; the values are regular expressions.") + + +;;; Updating a node + +;;;###autoload +(defun texinfo-update-node (&optional region-p) + "Without any prefix argument, update the node in which point is located. +Non-nil argument (prefix, if interactive) means update the nodes in the +marked region. + +The functions for creating or updating nodes and menus, and their +keybindings, are: + + texinfo-update-node (&optional region-p) \\[texinfo-update-node] + texinfo-every-node-update () \\[texinfo-every-node-update] + texinfo-sequential-node-update (&optional region-p) + + texinfo-make-menu (&optional region-p) \\[texinfo-make-menu] + texinfo-all-menus-update () \\[texinfo-all-menus-update] + texinfo-master-menu () + + texinfo-indent-menu-description (column &optional region-p) + +The `texinfo-column-for-description' variable specifies the column to +which menu descriptions are indented. Its default value is 32." + + (interactive "P") + (if (not region-p) + ;; update a single node + (let ((auto-fill-function nil) (auto-fill-hook nil)) + (if (not (re-search-backward "^@node" (point-min) t)) + (error "Node line not found before this position.")) + (texinfo-update-the-node) + (message "Done...updated the node. You may save the buffer.")) + ;; else + (let ((auto-fill-function nil) + (auto-fill-hook nil) + (beginning (region-beginning)) + (end (region-end))) + (if (= end beginning) + (error "Please mark a region!")) + (save-restriction + (narrow-to-region beginning end) + (goto-char beginning) + (push-mark (point) t) + (while (re-search-forward "^@node" (point-max) t) + (beginning-of-line) + (texinfo-update-the-node)) + (message "Done...updated nodes in region. You may save the buffer."))))) + +;;;###autoload +(defun texinfo-every-node-update () + "Update every node in a Texinfo file." + (interactive) + (save-excursion + (push-mark (point-max) t) + (goto-char (point-min)) + ;; Using the mark to pass bounds this way + ;; is kludgy, but it's not worth fixing. -- rms. + (let ((mark-active t)) + (texinfo-update-node t)) + (message "Done...updated every node. You may save the buffer."))) + +(defun texinfo-update-the-node () + "Update one node. Point must be at the beginning of node line. +Leave point at the end of the node line." + (texinfo-check-for-node-name) + (texinfo-delete-existing-pointers) + (message "Updating node: %s ... " (texinfo-copy-node-name)) + (save-restriction + (widen) + (let* + ((case-fold-search t) + (level (texinfo-hierarchic-level)) + (beginning (texinfo-update-menu-region-beginning level)) + (end (texinfo-update-menu-region-end level))) + (if (string-equal level "top") + (texinfo-top-pointer-case) + ;; else + (texinfo-insert-pointer beginning end level 'next) + (texinfo-insert-pointer beginning end level 'previous) + (texinfo-insert-pointer beginning end level 'up) + (texinfo-clean-up-node-line))))) + +(defun texinfo-top-pointer-case () + "Insert pointers in the Top node. This is a special case. + +The `Next' pointer is a pointer to a chapter or section at a lower +hierarchical level in the file. The `Previous' and `Up' pointers are +to `(dir)'. Point must be at the beginning of the node line, and is +left at the end of the node line." + + (texinfo-clean-up-node-line) + (insert ", " + (save-excursion + ;; There may be an @chapter or other such command between + ;; the top node line and the next node line, as a title + ;; for an `ifinfo' section. This @chapter command must + ;; must be skipped. So the procedure is to search for + ;; the next `@node' line, and then copy its name. + (if (re-search-forward "^@node" nil t) + (progn + (beginning-of-line) + (texinfo-copy-node-name)) + " ")) + ", (dir), (dir)")) + +(defun texinfo-check-for-node-name () + "Determine whether the node has a node name. Prompt for one if not. +Point must be at beginning of node line. Does not move point." + (save-excursion + (let ((initial (texinfo-copy-next-section-title))) + ;; This is not clean. Use `interactive' to read the arg. + (forward-word 1) ; skip over node command + (skip-chars-forward " \t") ; and over spaces + (if (not (looking-at "[^,\t\n ]+")) ; regexp based on what Info looks for + ; alternatively, use "[a-zA-Z]+" + (let ((node-name + (read-from-minibuffer + "Node name (use no @, commas, colons, or apostrophes): " + initial))) + (insert " " node-name)))))) + +(defun texinfo-delete-existing-pointers () + "Delete `Next', `Previous', and `Up' pointers. +Starts from the current position of the cursor, and searches forward +on the line for a comma and if one is found, deletes the rest of the +line, including the comma. Leaves point at beginning of line." + (let ((eol-point (save-excursion (end-of-line) (point)))) + (if (search-forward "," eol-point t) + (delete-region (1- (point)) eol-point))) + (beginning-of-line)) + +(defun texinfo-find-pointer (beginning end level direction) + "Move point to section associated with next, previous, or up pointer. +Return type of pointer (either 'normal or 'no-pointer). + +The first and second arguments bound the search for a pointer to the +beginning and end, respectively, of the enclosing higher level +section. The third argument is a string specifying the general kind +of section such as \"chapter\ or \"section\". When looking for the +`Next' pointer, the section found will be at the same hierarchical +level in the Texinfo file; when looking for the `Previous' pointer, +the section found will be at the same or higher hierarchical level in +the Texinfo file; when looking for the `Up' pointer, the section found +will be at some level higher in the Texinfo file. The fourth argument +\(one of 'next, 'previous, or 'up\) specifies whether to find the +`Next', `Previous', or `Up' pointer." + (let ((case-fold-search t)) + (cond ((eq direction 'next) + (forward-line 3) ; skip over current node + ;; Search for section commands accompanied by node lines; + ;; ignore section commands in the middle of nodes. + (if (re-search-forward + ;; A `Top' node is never a next pointer, so won't find it. + (concat + ;; Match node line. + "\\(^@node\\).*\n" + ;; Match comment or ifinfo line, if any + "\\(\\(\\(^@c\\).*\n\\)\\|\\(^@ifinfo[ ]*\n\\)\\)?" + (eval + (cdr (assoc level texinfo-update-menu-same-level-regexps)))) + end + t) + 'normal + 'no-pointer)) + ((eq direction 'previous) + (if (re-search-backward + (concat + "\\(" + ;; Match node line. + "\\(^@node\\).*\n" + ;; Match comment or ifinfo line, if any + "\\(\\(\\(^@c\\).*\n\\)\\|\\(^@ifinfo[ ]*\n\\)\\)?" + (eval + (cdr (assoc level texinfo-update-menu-same-level-regexps))) + "\\|" + ;; Match node line. + "\\(^@node\\).*\n" + ;; Match comment or ifinfo line, if any + "\\(\\(\\(^@c\\).*\n\\)\\|\\(^@ifinfo[ ]*\n\\)\\)?" + (eval + (cdr (assoc level texinfo-update-menu-higher-regexps))) + "\\|" + ;; Handle `Top' node specially. + "^@node [ \t]*top[ \t]*\\(,\\|$\\)" + "\\)") + beginning + t) + 'normal + 'no-pointer)) + ((eq direction 'up) + (if (re-search-backward + (concat + "\\(" + ;; Match node line. + "\\(^@node\\).*\n" + ;; Match comment or ifinfo line, if any + "\\(\\(\\(^@c\\).*\n\\)\\|\\(^@ifinfo[ ]*\n\\)\\)?" + (eval (cdr (assoc level texinfo-update-menu-higher-regexps))) + "\\|" + ;; Handle `Top' node specially. + "^@node [ \t]*top[ \t]*\\(,\\|$\\)" + "\\)") + (save-excursion + (goto-char beginning) + (beginning-of-line) + (point)) + t) + 'normal + 'no-pointer)) + (t + (error "texinfo-find-pointer: lack proper arguments"))))) + +(defun texinfo-pointer-name (kind) + "Return the node name preceding the section command. +The argument is the kind of section, either normal or no-pointer." + (let (name) + (cond ((eq kind 'normal) + (end-of-line) ; this handles prev node top case + (re-search-backward ; when point is already + "^@node" ; at the beginning of @node line + (save-excursion (forward-line -3)) + t) + (setq name (texinfo-copy-node-name))) + ((eq kind 'no-pointer) + (setq name " "))) ; put a blank in the pointer slot + name)) + +(defun texinfo-insert-pointer (beginning end level direction) + "Insert the `Next', `Previous' or `Up' node name at point. +Move point forward. + +The first and second arguments bound the search for a pointer to the +beginning and end, respectively, of the enclosing higher level +section. The third argument is the hierarchical level of the Texinfo +file, a string such as \"section\". The fourth argument is direction +towards which the pointer is directed, one of `next, `previous, or +'up." + + (end-of-line) + (insert + ", " + (save-excursion + (texinfo-pointer-name + (texinfo-find-pointer beginning end level direction))))) + +(defun texinfo-clean-up-node-line () + "Remove extra commas, if any, at end of node line." + (end-of-line) + (skip-chars-backward ", ") + (delete-region (point) (save-excursion (end-of-line) (point)))) + + +;;; Updating nodes sequentially +; These sequential update functions insert `Next' or `Previous' +; pointers that point to the following or preceding nodes even if they +; are at higher or lower hierarchical levels. This means that if a +; section contains one or more subsections, the section's `Next' +; pointer will point to the subsection and not the following section. +; (The subsection to which `Next' points will most likely be the first +; item on the section's menu.) + +;;;###autoload +(defun texinfo-sequential-node-update (&optional region-p) + "Update one node (or many) in a Texinfo file with sequential pointers. + +This function causes the `Next' or `Previous' pointer to point to the +immediately preceding or following node, even if it is at a higher or +lower hierarchical level in the document. Continually pressing `n' or +`p' takes you straight through the file. + +Without any prefix argument, update the node in which point is located. +Non-nil argument (prefix, if interactive) means update the nodes in the +marked region. + +This command makes it awkward to navigate among sections and +subsections; it should be used only for those documents that are meant +to be read like a novel rather than a reference, and for which the +Info `g*' command is inadequate." + + (interactive "P") + (if (not region-p) + ;; update a single node + (let ((auto-fill-function nil) (auto-fill-hook nil)) + (if (not (re-search-backward "^@node" (point-min) t)) + (error "Node line not found before this position.")) + (texinfo-sequentially-update-the-node) + (message + "Done...sequentially updated the node . You may save the buffer.")) + ;; else + (let ((auto-fill-function nil) + (auto-fill-hook nil) + (beginning (region-beginning)) + (end (region-end))) + (if (= end beginning) + (error "Please mark a region!")) + (save-restriction + (narrow-to-region beginning end) + (goto-char beginning) + (push-mark (point) t) + (while (re-search-forward "^@node" (point-max) t) + (beginning-of-line) + (texinfo-sequentially-update-the-node)) + (message + "Done...updated the nodes in sequence. You may save the buffer."))))) + +(defun texinfo-sequentially-update-the-node () + "Update one node such that the pointers are sequential. +A `Next' or `Previous' pointer points to any preceding or following node, +regardless of its hierarchical level." + + (texinfo-check-for-node-name) + (texinfo-delete-existing-pointers) + (message + "Sequentially updating node: %s ... " (texinfo-copy-node-name)) + (save-restriction + (widen) + (let* + ((case-fold-search t) + (level (texinfo-hierarchic-level))) + (if (string-equal level "top") + (texinfo-top-pointer-case) + ;; else + (texinfo-sequentially-insert-pointer level 'next) + (texinfo-sequentially-insert-pointer level 'previous) + (texinfo-sequentially-insert-pointer level 'up) + (texinfo-clean-up-node-line))))) + +(defun texinfo-sequentially-find-pointer (level direction) + "Find next or previous pointer sequentially in Texinfo file, or up pointer. +Move point to section associated with the pointer. Find point even if +it is in a different section. + +Return type of pointer (either 'normal or 'no-pointer). + +The first argument is a string specifying the general kind of section +such as \"chapter\ or \"section\". The section found will be at the +same hierarchical level in the Texinfo file, or, in the case of the up +pointer, some level higher. The second argument (one of 'next, +'previous, or 'up) specifies whether to find the `Next', `Previous', +or `Up' pointer." + (let ((case-fold-search t)) + (cond ((eq direction 'next) + (forward-line 3) ; skip over current node + (if (re-search-forward + texinfo-section-types-regexp + (point-max) + t) + 'normal + 'no-pointer)) + ((eq direction 'previous) + (if (re-search-backward + texinfo-section-types-regexp + (point-min) + t) + 'normal + 'no-pointer)) + ((eq direction 'up) + (if (re-search-backward + (eval (cdr (assoc level texinfo-update-menu-higher-regexps))) + beginning + t) + 'normal + 'no-pointer)) + (t + (error "texinfo-sequential-find-pointer: lack proper arguments"))))) + +(defun texinfo-sequentially-insert-pointer (level direction) + "Insert the `Next', `Previous' or `Up' node name at point. +Move point forward. + +The first argument is the hierarchical level of the Texinfo file, a +string such as \"section\". The second argument is direction, one of +`next, `previous, or 'up." + + (end-of-line) + (insert + ", " + (save-excursion + (texinfo-pointer-name + (texinfo-sequentially-find-pointer level direction))))) + + +;;; Inserting `@node' lines +; The `texinfo-insert-node-lines' function inserts `@node' lines as needed +; before the `@chapter', `@section', and such like lines of a region +; in a Texinfo file. + +(defun texinfo-insert-node-lines (beginning end &optional title-p) + "Insert missing `@node' lines in region of Texinfo file. +Non-nil argument (prefix, if interactive) means also to insert the +section titles as node names; and also to insert the section titles as +node names in pre-existing @node lines that lack names." + (interactive "r\nP") + + ;; Use marker; after inserting node lines, leave point at end of + ;; region and mark at beginning. + + (let (beginning-marker end-marker title last-section-position) + + ;; Save current position on mark ring and set mark to end. + (push-mark end t) + (setq end-marker (mark-marker)) + + (goto-char beginning) + (while (re-search-forward + texinfo-section-types-regexp + end-marker + 'end) + ;; Copy title if desired. + (if title-p + (progn + (beginning-of-line) + (forward-word 1) + (skip-chars-forward " \t") + (setq title (buffer-substring + (point) + (save-excursion (end-of-line) (point)))))) + ;; Insert node line if necessary. + (if (re-search-backward + "^@node" + ;; Avoid finding previous node line if node lines are close. + (or last-section-position + (save-excursion (forward-line -2) (point))) t) + ;; @node is present, and point at beginning of that line + (forward-word 1) ; Leave point just after @node. + ;; Else @node missing; insert one. + (beginning-of-line) ; Beginning of `@section' line. + (insert "@node\n") + (backward-char 1)) ; Leave point just after `@node'. + ;; Insert title if desired. + (if title-p + (progn + (skip-chars-forward " \t") + ;; Use regexp based on what info looks for + ;; (alternatively, use "[a-zA-Z]+"); + ;; this means we only insert a title if none exists. + (if (not (looking-at "[^,\t\n ]+")) + (progn + (beginning-of-line) + (forward-word 1) + (insert " " title) + (message "Inserted title %s ... " title))))) + ;; Go forward beyond current section title. + (re-search-forward texinfo-section-types-regexp + (save-excursion (forward-line 3) (point)) t) + (setq last-section-position (point)) + (forward-line 1)) + + ;; Leave point at end of region, mark at beginning. + (set-mark beginning) + + (if title-p + (message + "Done inserting node lines and titles. You may save the buffer.") + (message "Done inserting node lines. You may save the buffer.")))) + + +;;; Update and create menus for multi-file Texinfo sources + +;; 1. M-x texinfo-multiple-files-update +;; +;; Read the include file list of an outer Texinfo file and +;; update all highest level nodes in the files listed and insert a +;; main menu in the outer file after its top node. + +;; 2. C-u M-x texinfo-multiple-files-update +;; +;; Same as 1, but insert a master menu. (Saves reupdating lower +;; level menus and nodes.) This command simply reads every menu, +;; so if the menus are wrong, the master menu will be wrong. +;; Similarly, if the lower level node pointers are wrong, they +;; will stay wrong. + +;; 3. C-u 2 M-x texinfo-multiple-files-update +;; +;; Read the include file list of an outer Texinfo file and +;; update all nodes and menus in the files listed and insert a +;; master menu in the outer file after its top node. + +;;; Note: these functions: +;;; +;;; * Do not save or delete any buffers. You may fill up your memory. +;;; * Do not handle any pre-existing nodes in outer file. +;;; Hence, you may need a file for indices. + + +;;; Auxiliary functions for multiple file updating + +(defun texinfo-multi-file-included-list (outer-file) + "Return a list of the included files in OUTER-FILE." + (let ((included-file-list (list outer-file)) + start) + (save-excursion + (switch-to-buffer (find-file-noselect outer-file)) + (widen) + (goto-char (point-min)) + (while (re-search-forward "^@include" nil t) + (skip-chars-forward " \t") + (setq start (point)) + (end-of-line) + (skip-chars-backward " \t") + (setq included-file-list + (cons (buffer-substring start (point)) + included-file-list))) + (nreverse included-file-list)))) + +(defun texinfo-copy-next-section-title () + "Return the name of the immediately following section as a string. + +Start with point at the beginning of the node line. Leave point at the +same place. If there is no title, returns an empty string." + + (save-excursion + (end-of-line) + (let ((node-end (or + (save-excursion + (if (re-search-forward "\\(^@node\\)" nil t) + (match-beginning 0))) + (point-max)))) + (if (re-search-forward texinfo-section-types-regexp node-end t) + (progn + (beginning-of-line) + ;; copy title + (let ((title + (buffer-substring + (progn (forward-word 1) ; skip over section type + (skip-chars-forward " \t") ; and over spaces + (point)) + (progn (end-of-line) (point))))) + title)) + "")))) + +(defun texinfo-multi-file-update (files &optional update-everything) + "Update first node pointers in each file in FILES. +Return a list of the node names. + +The first file in the list is an outer file; the remaining are +files included in the outer file with `@include' commands. + +If optional arg UPDATE-EVERYTHING non-nil, update every menu and +pointer in each of the included files. + +Also update the `Top' level node pointers of the outer file. + +Requirements: + + * the first file in the FILES list must be the outer file, + * each of the included files must contain exactly one highest + hierarchical level node, + * this node must be the first node in the included file, + * each highest hierarchical level node must be of the same type. + +Thus, normally, each included file contains one, and only one, +chapter." + +; The menu-list has the form: +; +; \(\(\"node-name1\" . \"title1\"\) +; \(\"node-name2\" . \"title2\"\) ... \) +; +; However, there does not need to be a title field and this function +; does not fill it; however a comment tells you how to do so. +; You would use the title field if you wanted to insert titles in the +; description slot of a menu as a description. + + (let ((case-fold-search t) + menu-list) + + ;; Find the name of the first node of the first included file. + (switch-to-buffer (find-file-noselect (car (cdr files)))) + (widen) + (goto-char (point-min)) + (if (not (re-search-forward "^@node" nil t)) + (error "No `@node' line found in %s !" (buffer-name))) + (beginning-of-line) + (texinfo-check-for-node-name) + (setq next-node-name (texinfo-copy-node-name)) + + (setq menu-list + (cons (cons + next-node-name + (prog1 "" (forward-line 1))) + ;; Use following to insert section titles automatically. + ;; (texinfo-copy-next-section-title) + menu-list)) + + ;; Go to outer file + (switch-to-buffer (find-file-noselect (car files))) + (goto-char (point-min)) + (if (not (re-search-forward "^@node [ \t]*top[ \t]*\\(,\\|$\\)" nil t)) + (error "This buffer needs a Top node!")) + (beginning-of-line) + (texinfo-delete-existing-pointers) + (end-of-line) + (insert ", " next-node-name ", (dir), (dir)") + (beginning-of-line) + (setq previous-node-name "Top") + (setq files (cdr files)) + + (while files + + (if (not (cdr files)) + ;; No next file + (setq next-node-name "") + ;; Else, + ;; find the name of the first node in the next file. + (switch-to-buffer (find-file-noselect (car (cdr files)))) + (widen) + (goto-char (point-min)) + (if (not (re-search-forward "^@node" nil t)) + (error "No `@node' line found in %s !" (buffer-name))) + (beginning-of-line) + (texinfo-check-for-node-name) + (setq next-node-name (texinfo-copy-node-name)) + (setq menu-list + (cons (cons + next-node-name + (prog1 "" (forward-line 1))) + ;; Use following to insert section titles automatically. + ;; (texinfo-copy-next-section-title) + menu-list))) + + ;; Go to node to be updated. + (switch-to-buffer (find-file-noselect (car files))) + (goto-char (point-min)) + (if (not (re-search-forward "^@node" nil t)) + (error "No `@node' line found in %s !" (buffer-name))) + (beginning-of-line) + + ;; Update other menus and nodes if requested. + (if update-everything (texinfo-all-menus-update t)) + + (beginning-of-line) + (texinfo-delete-existing-pointers) + (end-of-line) + (insert ", " next-node-name ", " previous-node-name ", " up-node-name) + + (beginning-of-line) + (setq previous-node-name (texinfo-copy-node-name)) + + (setq files (cdr files))) + (nreverse menu-list))) + +(defun texinfo-multi-files-insert-main-menu (menu-list) + "Insert formatted main menu at point. +Indents the first line of the description, if any, to the value of +texinfo-column-for-description." + + (insert "@menu\n") + (while menu-list + ;; Every menu entry starts with a star and a space. + (insert "* ") + + ;; Insert the node name (and menu entry name, if present). + (let ((node-part (car (car menu-list)))) + (if (stringp node-part) + ;; "Double colon" entry line; menu entry and node name are the same, + (insert (format "%s::" node-part)) + ;; "Single colon" entry line; menu entry and node name are different. + (insert (format "%s: %s." (car node-part) (cdr node-part))))) + + ;; Insert the description, if present. + (if (cdr (car menu-list)) + (progn + ;; Move to right place. + (indent-to texinfo-column-for-description 2) + ;; Insert description. + (insert (format "%s" (cdr (car menu-list)))))) + + (insert "\n") ; end this menu entry + (setq menu-list (cdr menu-list))) + (insert "@end menu")) + +(defun texinfo-multi-file-master-menu-list (files-list) + "Return master menu list from files in FILES-LIST. +Menu entries in each file collected using `texinfo-master-menu-list'. + +The first file in FILES-LIST must be the outer file; the others must +be the files included within it. A main menu must already exist." + (save-excursion + (let (master-menu-list) + (while files-list + (switch-to-buffer (find-file-noselect (car files-list))) + (message "Working on: %s " (current-buffer)) + (goto-char (point-min)) + (setq master-menu-list + (append master-menu-list (texinfo-master-menu-list))) + (setq files-list (cdr files-list))) + master-menu-list))) + + +;;; The multiple-file update function + +(defun texinfo-multiple-files-update + (outer-file &optional update-everything make-master-menu) + "Update first node pointers in each file included in OUTER-FILE; +create or update the `Top' level node pointers and the main menu in +the outer file that refers to such nodes. This does not create or +update menus or pointers within the included files. + +With optional MAKE-MASTER-MENU argument (prefix arg, if interactive), +insert a master menu in OUTER-FILE in addition to creating or updating +pointers in the first @node line in each included file and creating or +updating the `Top' level node pointers of the outer file. This does +not create or update other menus and pointers within the included +files. + +With optional UPDATE-EVERYTHING argument (numeric prefix arg, if +interactive), update all the menus and all the `Next', `Previous', and +`Up' pointers of all the files included in OUTER-FILE before inserting +a master menu in OUTER-FILE. Also, update the `Top' level node +pointers of OUTER-FILE. + +Notes: + + * this command does NOT save any files--you must save the + outer file and any modified, included files. + + * except for the `Top' node, this command does NOT handle any + pre-existing nodes in the outer file; hence, indices must be + enclosed in an included file. + +Requirements: + + * each of the included files must contain exactly one highest + hierarchical level node, + * this highest node must be the first node in the included file, + * each highest hierarchical level node must be of the same type. + +Thus, normally, each included file contains one, and only one, +chapter." + + (interactive (cons + (read-string + "Name of outer `include' file: " + (buffer-file-name)) + (cond ((not current-prefix-arg) + '(nil nil)) + ((listp current-prefix-arg) + '(t nil)) ; make-master-menu + ((numberp current-prefix-arg) + '(t t)) ; update-everything + ))) + + (let* ((included-file-list (texinfo-multi-file-included-list outer-file)) + (files included-file-list) + main-menu-list + next-node-name + previous-node-name + (up-node-name "Top")) + +;;; Update the pointers +;;; and collect the names of the nodes and titles + (setq main-menu-list (texinfo-multi-file-update files update-everything)) + +;;; Insert main menu + + ;; Go to outer file + (switch-to-buffer (find-file-noselect (car included-file-list))) + (if (texinfo-old-menu-p + (point-min) + (save-excursion + (re-search-forward "^@include") + (beginning-of-line) + (point))) + + ;; If found, leave point after word `menu' on the `@menu' line. + (progn + (texinfo-incorporate-descriptions main-menu-list) + ;; Delete existing menu. + (beginning-of-line) + (delete-region + (point) + (save-excursion (re-search-forward "^@end menu") (point))) + ;; Insert main menu + (texinfo-multi-files-insert-main-menu main-menu-list)) + + ;; Else no current menu; insert it before `@include' + (texinfo-multi-files-insert-main-menu main-menu-list)) + +;;; Insert master menu + + (if make-master-menu + (progn + ;; First, removing detailed part of any pre-existing master menu + (goto-char (point-min)) + (if (re-search-forward texinfo-master-menu-header nil t) + ;; Remove detailed master menu listing + (progn + (goto-char (match-beginning 0)) + (let ((end-of-detailed-menu-descriptions + (save-excursion ; beginning of end menu line + (goto-char (texinfo-menu-end)) + (beginning-of-line) (forward-char -1) + (point)))) + (delete-region (point) end-of-detailed-menu-descriptions)))) + + ;; Create a master menu and insert it + (texinfo-insert-master-menu-list + (texinfo-multi-file-master-menu-list + included-file-list))))) + + ;; Remove unwanted extra lines. + (save-excursion + (goto-char (point-min)) + + (re-search-forward "^@menu") + (forward-line -1) + (insert "\n") ; Ensure at least one blank line. + (delete-blank-lines) + + (re-search-forward "^@end menu") + (forward-line 1) + (insert "\n") ; Ensure at least one blank line. + (delete-blank-lines)) + + (message "Multiple files updated.")) + + +;;; Place `provide' at end of file. +(provide 'texnfo-upd) + +;;; texnfo-upd.el ends here |