| ;;; texnfo-upd.el --- utilities for updating nodes and menus in Texinfo files |
| |
| ;; Copyright 1989, 1990, 1991, 1992, 1996 Free Software Foundation, Inc. |
| |
| ;; Author: Robert J. Chassell |
| ;; Date: 12 Sep 1996 |
| ;; Maintainer: Robert J. Chassell <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, Inc., 59 Temple Place - Suite 330, |
| ;; Boston, MA 02111-1307, 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-existing 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)) |
| ;; @detailmenu added 5 Sept 1996 at Karl Berry's request to avert a |
| ;; bug in `makeinfo'; all agree this is a bad kluge and should |
| ;; eventually be removed. @detailmenu ... @end detailmenu is a noop |
| ;; in `texinfmt.el' See @end detailmenu below |
| ;; also see `texinfo-all-menus-update' above, `texinfo-master-menu', |
| ;; `texinfo-multiple-files-update' |
| (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 |
| ;; @detailmenu (see note above) |
| (insert "\n@end detailmenu") |
| (insert "\n@end menu\n\n"))) |
| |
| (defvar texinfo-master-menu-header |
| "\n@detailmenu\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-no-properties |
| (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 |