| 1 | ;;; makeinfo.el --- run makeinfo conveniently |
| 2 | |
| 3 | ;; Copyright (C) 1991, 1993, 2002, 2003, 2004, |
| 4 | ;; 2005 Free Software Foundation, Inc. |
| 5 | |
| 6 | ;; Author: Robert J. Chassell |
| 7 | ;; Maintainer: FSF |
| 8 | ;; Keywords: docs convenience |
| 9 | |
| 10 | ;; This file is part of GNU Emacs. |
| 11 | |
| 12 | ;; GNU Emacs is free software; you can redistribute it and/or modify |
| 13 | ;; it under the terms of the GNU General Public License as published by |
| 14 | ;; the Free Software Foundation; either version 2, or (at your option) |
| 15 | ;; any later version. |
| 16 | |
| 17 | ;; GNU Emacs is distributed in the hope that it will be useful, |
| 18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 20 | ;; GNU General Public License for more details. |
| 21 | |
| 22 | ;; You should have received a copy of the GNU General Public License |
| 23 | ;; along with GNU Emacs; see the file COPYING. If not, write to the |
| 24 | ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 25 | ;; Boston, MA 02110-1301, USA. |
| 26 | |
| 27 | ;;; Commentary: |
| 28 | |
| 29 | ;;; The Texinfo mode `makeinfo' related commands are: |
| 30 | |
| 31 | ;; makeinfo-region to run makeinfo on the current region. |
| 32 | ;; makeinfo-buffer to run makeinfo on the current buffer, or |
| 33 | ;; with optional prefix arg, on current region |
| 34 | ;; kill-compilation to kill currently running makeinfo job |
| 35 | ;; makeinfo-recenter-makeinfo-buffer to redisplay *compilation* buffer |
| 36 | |
| 37 | ;;; Keybindings (defined in `texinfo.el') |
| 38 | |
| 39 | ;; makeinfo bindings |
| 40 | ; (define-key texinfo-mode-map "\C-c\C-m\C-r" 'makeinfo-region) |
| 41 | ; (define-key texinfo-mode-map "\C-c\C-m\C-b" 'makeinfo-buffer) |
| 42 | ; (define-key texinfo-mode-map "\C-c\C-m\C-k" 'kill-compilation) |
| 43 | ; (define-key texinfo-mode-map "\C-c\C-m\C-l" |
| 44 | ; 'makeinfo-recenter-compilation-buffer) |
| 45 | \f |
| 46 | ;;; Code: |
| 47 | |
| 48 | ;;; Variables used by `makeinfo' |
| 49 | |
| 50 | (require 'compile) |
| 51 | (require 'info) |
| 52 | |
| 53 | (defgroup makeinfo nil |
| 54 | "Run makeinfo conveniently." |
| 55 | :group 'docs) |
| 56 | |
| 57 | |
| 58 | (defcustom makeinfo-run-command "makeinfo" |
| 59 | "*Command used to run `makeinfo' subjob. |
| 60 | The name of the file is appended to this string, separated by a space." |
| 61 | :type 'string |
| 62 | :group 'makeinfo) |
| 63 | |
| 64 | (defcustom makeinfo-options "--fill-column=70" |
| 65 | "*String containing options for running `makeinfo'. |
| 66 | Do not include `--footnote-style' or `--paragraph-indent'; |
| 67 | the proper way to specify those is with the Texinfo commands |
| 68 | `@footnotestyle` and `@paragraphindent'." |
| 69 | :type 'string |
| 70 | :group 'makeinfo) |
| 71 | |
| 72 | (require 'texinfo) |
| 73 | |
| 74 | (defvar makeinfo-compilation-process nil |
| 75 | "Process that runs `makeinfo'. Should start out nil.") |
| 76 | |
| 77 | (defvar makeinfo-temp-file nil |
| 78 | "Temporary file name used for text being sent as input to `makeinfo'.") |
| 79 | |
| 80 | (defvar makeinfo-output-file-name nil |
| 81 | "Info file name used for text output by `makeinfo'.") |
| 82 | |
| 83 | (defvar makeinfo-output-node-name nil |
| 84 | "Node name to visit in output file, for `makeinfo-buffer'.") |
| 85 | |
| 86 | \f |
| 87 | ;;; The `makeinfo' function definitions |
| 88 | |
| 89 | (defun makeinfo-region (region-beginning region-end) |
| 90 | "Make Info file from region of current Texinfo file, and switch to it. |
| 91 | |
| 92 | This command does not offer the `next-error' feature since it would |
| 93 | apply to a temporary file, not the original; use the `makeinfo-buffer' |
| 94 | command to gain use of `next-error'." |
| 95 | |
| 96 | (interactive "r") |
| 97 | (let (filename-or-header |
| 98 | filename-or-header-beginning |
| 99 | filename-or-header-end) |
| 100 | ;; Cannot use `let' for makeinfo-temp-file or |
| 101 | ;; makeinfo-output-file-name since `makeinfo-compilation-sentinel' |
| 102 | ;; needs them. |
| 103 | |
| 104 | (setq makeinfo-temp-file |
| 105 | (concat |
| 106 | (make-temp-file |
| 107 | (substring (buffer-file-name) |
| 108 | 0 |
| 109 | (or (string-match "\\.tex" (buffer-file-name)) |
| 110 | (length (buffer-file-name))))) |
| 111 | ".texinfo")) |
| 112 | |
| 113 | (save-excursion |
| 114 | (save-restriction |
| 115 | (widen) |
| 116 | (goto-char (point-min)) |
| 117 | (let ((search-end (save-excursion (forward-line 100) (point)))) |
| 118 | ;; Find and record the Info filename, |
| 119 | ;; or else explain that a filename is needed. |
| 120 | (if (re-search-forward |
| 121 | "^@setfilename[ \t]+\\([^ \t\n]+\\)[ \t]*" |
| 122 | search-end t) |
| 123 | (setq makeinfo-output-file-name |
| 124 | (buffer-substring (match-beginning 1) (match-end 1))) |
| 125 | (error |
| 126 | "The texinfo file needs a line saying: @setfilename <name>")) |
| 127 | |
| 128 | ;; Find header and specify its beginning and end. |
| 129 | (goto-char (point-min)) |
| 130 | (if (and |
| 131 | (prog1 |
| 132 | (search-forward tex-start-of-header search-end t) |
| 133 | (beginning-of-line) |
| 134 | ;; Mark beginning of header. |
| 135 | (setq filename-or-header-beginning (point))) |
| 136 | (prog1 |
| 137 | (search-forward tex-end-of-header nil t) |
| 138 | (beginning-of-line) |
| 139 | ;; Mark end of header |
| 140 | (setq filename-or-header-end (point)))) |
| 141 | |
| 142 | ;; Insert the header into the temporary file. |
| 143 | (write-region |
| 144 | (min filename-or-header-beginning region-beginning) |
| 145 | filename-or-header-end |
| 146 | makeinfo-temp-file nil nil) |
| 147 | |
| 148 | ;; Else no header; insert @filename line into temporary file. |
| 149 | (goto-char (point-min)) |
| 150 | (search-forward "@setfilename" search-end t) |
| 151 | (beginning-of-line) |
| 152 | (setq filename-or-header-beginning (point)) |
| 153 | (forward-line 1) |
| 154 | (setq filename-or-header-end (point)) |
| 155 | (write-region |
| 156 | (min filename-or-header-beginning region-beginning) |
| 157 | filename-or-header-end |
| 158 | makeinfo-temp-file nil nil)) |
| 159 | |
| 160 | ;; Insert the region into the file. |
| 161 | (write-region |
| 162 | (max region-beginning filename-or-header-end) |
| 163 | region-end |
| 164 | makeinfo-temp-file t nil) |
| 165 | |
| 166 | ;; Run the `makeinfo-compile' command in the *compilation* buffer |
| 167 | (save-excursion |
| 168 | (makeinfo-compile |
| 169 | (concat makeinfo-run-command |
| 170 | " " |
| 171 | makeinfo-options |
| 172 | " " |
| 173 | makeinfo-temp-file) |
| 174 | t |
| 175 | 'makeinfo-compilation-sentinel-region))))))) |
| 176 | |
| 177 | (defun makeinfo-next-error (arg reset) |
| 178 | "This function is used to disable `next-error' if the user has |
| 179 | used `makeinfo-region'. Since the compilation process is used on |
| 180 | a temporary file in that case, calling `next-error' would give |
| 181 | nonsensical results." |
| 182 | (error "Use `makeinfo-buffer' to gain use of the `next-error' command")) |
| 183 | |
| 184 | ;; Actually run makeinfo. COMMAND is the command to run. If |
| 185 | ;; DISABLE-ERRORS is non-nil, disable `next-error' by setting |
| 186 | ;; `next-error-function' to `makeinfo-next-error' in the compilation |
| 187 | ;; buffer. |
| 188 | (defun makeinfo-compile (command disable-errors sentinel) |
| 189 | (let ((buffer (compilation-start command))) |
| 190 | (with-current-buffer buffer |
| 191 | (setq next-error-function |
| 192 | (if disable-errors |
| 193 | 'makeinfo-next-error |
| 194 | 'compilation-next-error-function))) |
| 195 | (set-process-sentinel (get-buffer-process buffer) sentinel))) |
| 196 | |
| 197 | ;; Delete makeinfo-temp-file after processing is finished, |
| 198 | ;; and visit Info file. |
| 199 | ;; This function is called when the compilation process changes state. |
| 200 | ;; Based on `compilation-sentinel' in compile.el |
| 201 | (defun makeinfo-compilation-sentinel-region (proc msg) |
| 202 | "Sentinel for `makeinfo-compile' run from `makeinfo-region'." |
| 203 | (compilation-sentinel proc msg) |
| 204 | (when (memq (process-status proc) '(signal exit)) |
| 205 | (if (file-exists-p makeinfo-temp-file) |
| 206 | (delete-file makeinfo-temp-file)) |
| 207 | ;; Always use the version on disk. |
| 208 | (let ((buffer (get-file-buffer makeinfo-output-file-name))) |
| 209 | (if buffer |
| 210 | (with-current-buffer buffer |
| 211 | (revert-buffer t t)) |
| 212 | (setq buffer (find-file-noselect makeinfo-output-file-name))) |
| 213 | (if (window-dedicated-p (selected-window)) |
| 214 | (switch-to-buffer-other-window buffer) |
| 215 | (switch-to-buffer buffer))) |
| 216 | (goto-char (point-min)))) |
| 217 | |
| 218 | (defun makeinfo-current-node () |
| 219 | "Return the name of the node containing point, in a texinfo file." |
| 220 | (save-excursion |
| 221 | (end-of-line) ; in case point is at the start of an @node line |
| 222 | (if (re-search-backward "^@node\\s-+\\([^,\n]+\\)" (point-min) t) |
| 223 | (match-string 1) |
| 224 | "Top"))) |
| 225 | |
| 226 | (defun makeinfo-buffer () |
| 227 | "Make Info file from current buffer. |
| 228 | |
| 229 | Use the \\[next-error] command to move to the next error |
| 230 | \(if there are errors\)." |
| 231 | |
| 232 | (interactive) |
| 233 | (cond ((null buffer-file-name) |
| 234 | (error "Buffer not visiting any file")) |
| 235 | ((buffer-modified-p) |
| 236 | (if (y-or-n-p "Buffer modified; do you want to save it? ") |
| 237 | (save-buffer)))) |
| 238 | |
| 239 | ;; Find and record the Info filename, |
| 240 | ;; or else explain that a filename is needed. |
| 241 | (save-excursion |
| 242 | (goto-char (point-min)) |
| 243 | (let ((search-end (save-excursion (forward-line 100) (point)))) |
| 244 | (if (re-search-forward |
| 245 | "^@setfilename[ \t]+\\([^ \t\n]+\\)[ \t]*" |
| 246 | search-end t) |
| 247 | (setq makeinfo-output-file-name |
| 248 | (expand-file-name |
| 249 | (buffer-substring (match-beginning 1) (match-end 1)))) |
| 250 | (error |
| 251 | "The texinfo file needs a line saying: @setfilename <name>")))) |
| 252 | (setq makeinfo-output-node-name (makeinfo-current-node)) |
| 253 | |
| 254 | (save-excursion |
| 255 | (makeinfo-compile |
| 256 | (concat makeinfo-run-command " " makeinfo-options |
| 257 | " " buffer-file-name) |
| 258 | nil |
| 259 | 'makeinfo-compilation-sentinel-buffer))) |
| 260 | |
| 261 | (defun makeinfo-compilation-sentinel-buffer (proc msg) |
| 262 | "Sentinel for `makeinfo-compile' run from `makeinfo-buffer'." |
| 263 | (compilation-sentinel proc msg) |
| 264 | (when (memq (process-status proc) '(signal exit)) |
| 265 | (when (file-exists-p makeinfo-output-file-name) |
| 266 | (Info-revert-find-node |
| 267 | makeinfo-output-file-name makeinfo-output-node-name)))) |
| 268 | |
| 269 | (defun makeinfo-recenter-compilation-buffer (linenum) |
| 270 | "Redisplay `*compilation*' buffer so most recent output can be seen. |
| 271 | The last line of the buffer is displayed on |
| 272 | line LINE of the window, or centered if LINE is nil." |
| 273 | (interactive "P") |
| 274 | (let ((makeinfo-buffer (get-buffer "*compilation*")) |
| 275 | (old-buffer (current-buffer))) |
| 276 | (if (null makeinfo-buffer) |
| 277 | (message "No *compilation* buffer") |
| 278 | (pop-to-buffer makeinfo-buffer) |
| 279 | (bury-buffer makeinfo-buffer) |
| 280 | (goto-char (point-max)) |
| 281 | (recenter (if linenum |
| 282 | (prefix-numeric-value linenum) |
| 283 | (/ (window-height) 2))) |
| 284 | (pop-to-buffer old-buffer) |
| 285 | ))) |
| 286 | |
| 287 | ;;; Place `provide' at end of file. |
| 288 | (provide 'makeinfo) |
| 289 | |
| 290 | ;;; arch-tag: 5f810713-3de2-4e20-8030-4bc3dd0d9604 |
| 291 | ;;; makeinfo.el ends here |