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