lisp/textmodes/bibtex.el (bibtex-search-entries): Bug fix
[bpt/emacs.git] / lisp / textmodes / reftex-global.el
CommitLineData
3afbc435 1;;; reftex-global.el --- operations on entire documents with RefTeX
f2e3589a 2
ab422c4d 3;; Copyright (C) 1997-2013 Free Software Foundation, Inc.
3ba2590f 4
6fbeb429 5;; Author: Carsten Dominik <dominik@science.uva.nl>
ce545621 6;; Maintainer: auctex-devel@gnu.org
3ba2590f
RS
7
8;; This file is part of GNU Emacs.
9
1fecc8fe 10;; GNU Emacs is free software: you can redistribute it and/or modify
3ba2590f 11;; it under the terms of the GNU General Public License as published by
1fecc8fe
GM
12;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
3ba2590f
RS
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
1fecc8fe 21;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
1a9461d0 22
3afbc435
PJ
23;;; Commentary:
24
25;;; Code:
26
7c4d13cc 27(eval-when-compile (require 'cl))
1a9461d0
CD
28(provide 'reftex-global)
29(require 'reftex)
30;;;
31
32(defun reftex-create-tags-file ()
33 "Create TAGS file by running `etags' on the current document.
34The TAGS file is also immediately visited with `visit-tags-table'."
35 (interactive)
36 (reftex-access-scan-info current-prefix-arg)
37 (let* ((master (reftex-TeX-master-file))
38 (files (reftex-all-document-files))
773c795f
CD
39 (cmd (format "etags %s" (mapconcat 'shell-quote-argument
40 files " "))))
9a529312 41 (with-current-buffer (reftex-get-file-buffer-force master)
1a9461d0
CD
42 (message "Running etags to create TAGS file...")
43 (shell-command cmd)
44 (visit-tags-table "TAGS"))))
45
46;; History of grep commands.
47(defvar reftex-grep-history nil)
48(defvar reftex-grep-command "grep -n "
49 "Last grep command used in \\[reftex-grep-document]; default for next grep.")
50
51(defun reftex-grep-document (grep-cmd)
52 "Run grep query through all files related to this document.
53With prefix arg, force to rescan document.
54No active TAGS table is required."
55
56 (interactive
57 (list (read-from-minibuffer "Run grep on document (like this): "
58 reftex-grep-command nil nil
59 'reftex-grep-history)))
60 (reftex-access-scan-info current-prefix-arg)
61 (let* ((files (reftex-all-document-files t))
62 (cmd (format
63 "%s %s" grep-cmd
64 (mapconcat 'identity files " "))))
65 (grep cmd)))
66
67(defun reftex-search-document (&optional regexp)
68 "Regexp search through all files of the current document.
69Starts always in the master file. Stops when a match is found.
70To continue searching for next match, use command \\[tags-loop-continue].
71No active TAGS table is required."
72 (interactive)
73 (let ((default (reftex-this-word)))
74 (unless regexp
75 (setq regexp (read-string (format "Search regexp in document [%s]: "
76 default))))
77 (if (string= regexp "") (setq regexp (regexp-quote default)))
78
79 (reftex-access-scan-info current-prefix-arg)
80 (tags-search regexp (list 'reftex-all-document-files))))
81
82(defun reftex-query-replace-document (&optional from to delimited)
44fc58f2 83 "Do `query-replace-regexp' of FROM with TO over the entire document.
1a9461d0 84Third arg DELIMITED (prefix arg) means replace only word-delimited matches.
44fc58f2 85If you exit (\\[keyboard-quit], RET or q), you can resume the query replace
1a9461d0
CD
86with the command \\[tags-loop-continue].
87No active TAGS table is required."
88 (interactive)
89 (let ((default (reftex-this-word)))
90 (unless from
91 (setq from (read-string (format "Replace regexp in document [%s]: "
92 default)))
93 (if (string= from "") (setq from (regexp-quote default))))
94 (unless to
95 (setq to (read-string (format "Replace regexp %s with: " from))))
96 (reftex-access-scan-info current-prefix-arg)
97 (tags-query-replace from to (or delimited current-prefix-arg)
98 (list 'reftex-all-document-files))))
99
0c86715d
DN
100(defvar TeX-master)
101(defvar isearch-next-buffer-function)
7b07114a 102
1a9461d0
CD
103(defun reftex-find-duplicate-labels ()
104 "Produce a list of all duplicate labels in the document."
105
106 (interactive)
107
108 ;; Rescan the document to make sure
109 (reftex-access-scan-info t)
110
111 (let ((master (reftex-TeX-master-file))
3666daf6 112 (cnt 0)
1a9461d0
CD
113 (dlist
114 (mapcar
3666daf6
CD
115 (lambda (x)
116 (let (x1)
117 (cond
118 ((memq (car x)
119 '(toc bof eof bib thebib label-numbers xr xr-doc
120 master-dir file-error bibview-cache appendix
121 is-multi index))
122 nil)
123 (t
124 (setq x1 (reftex-all-assoc-string
125 (car x) (symbol-value reftex-docstruct-symbol)))
126 (if (< 1 (length x1))
127 (append (list (car x))
128 (mapcar (lambda(x)
129 (abbreviate-file-name (nth 3 x)))
130 x1))
131 (list nil))))))
1a9461d0
CD
132 (reftex-uniquify-by-car (symbol-value reftex-docstruct-symbol)))))
133
134 (setq dlist (reftex-uniquify-by-car dlist))
135 (if (null dlist) (error "No duplicate labels in document"))
136 (switch-to-buffer-other-window "*Duplicate Labels*")
137 (set (make-local-variable 'TeX-master) master)
138 (erase-buffer)
139 (insert " MULTIPLE LABELS IN CURRENT DOCUMENT:\n")
09e80d9f 140 (insert
1a9461d0
CD
141 " Move point to label and type `r' to run a query-replace on the label\n"
142 " and its references. Type `q' to exit this buffer.\n\n")
143 (insert " LABEL FILE\n")
144 (insert " -------------------------------------------------------------\n")
145 (use-local-map (make-sparse-keymap))
146 (local-set-key [?q] (lambda () "Kill this buffer." (interactive)
3666daf6 147 (kill-buffer (current-buffer)) (delete-window)))
1a9461d0
CD
148 (local-set-key [?r] 'reftex-change-label)
149 (while dlist
150 (when (and (car (car dlist))
151 (cdr (car dlist)))
3666daf6 152 (incf cnt)
1a9461d0
CD
153 (insert (mapconcat 'identity (car dlist) "\n ") "\n"))
154 (pop dlist))
155 (goto-char (point-min))
156 (when (= cnt 0)
157 (kill-buffer (current-buffer))
158 (delete-window)
159 (message "Document does not contain duplicate labels."))))
160
161(defun reftex-change-label (&optional from to)
3666daf6 162 "Run `query-replace-regexp' of FROM with TO in all macro arguments.
1a9461d0 163Works on the entire multifile document.
44fc58f2 164If you exit (\\[keyboard-quit], RET or q), you can resume the query replace
1a9461d0
CD
165with the command \\[tags-loop-continue].
166No active TAGS table is required."
167 (interactive)
168 (let ((default (reftex-this-word "-a-zA-Z0-9_*.:")))
169 (unless from
170 (setq from (read-string (format "Replace label globally [%s]: "
171 default))))
172 (if (string= from "") (setq from default))
173 (unless to
174 (setq to (read-string (format "Replace label %s with: "
175 from))))
176 (reftex-query-replace-document
3666daf6
CD
177 (concat "{" (regexp-quote from) "}")
178 (format "{%s}" to))))
1a9461d0
CD
179
180(defun reftex-renumber-simple-labels ()
181 "Renumber all simple labels in the document to make them sequentially.
182Simple labels are the ones created by RefTeX, consisting only of the
183prefix and a number. After the command completes, all these labels will
184have sequential numbers throughout the document. Any references to
185the labels will be changed as well. For this, RefTeX looks at the
186arguments of any macros which either start or end in the string `ref'.
187This command should be used with care, in particular in multifile
188documents. You should not use it if another document refers to this
189one with the `xr' package."
190 (interactive)
09e80d9f 191 ;; Rescan the entire document
1a9461d0
CD
192 (reftex-access-scan-info 1)
193 ;; Get some insurance
194 (if (and (reftex-is-multi)
3666daf6 195 (not (yes-or-no-p "Replacing all simple labels in multiple files is risky. Continue? ")))
1a9461d0
CD
196 (error "Abort"))
197 ;; Make the translation list
09e80d9f
PE
198 (let* ((re-core (concat "\\("
199 (mapconcat 'cdr reftex-typekey-to-prefix-alist "\\|")
3666daf6
CD
200 "\\)"))
201 (label-re (concat "\\`" re-core "\\([0-9]+\\)\\'"))
202 (search-re (concat "[{,]\\(" re-core "\\([0-9]+\\)\\)[,}]"))
203 (error-fmt "Undefined label or reference %s. Ignore and continue? ")
204 (label-numbers-alist (mapcar (lambda (x) (cons (cdr x) 0))
205 reftex-typekey-to-prefix-alist))
206 (files (reftex-all-document-files))
207 (list (symbol-value reftex-docstruct-symbol))
208 translate-alist n entry label new-label nr-cell changed-sequence)
1a9461d0
CD
209
210 (while (setq entry (pop list))
211 (when (and (stringp (car entry))
3666daf6
CD
212 (string-match label-re (car entry)))
213 (setq label (car entry)
214 nr-cell (assoc (match-string 1 (car entry))
215 label-numbers-alist))
216 (if (assoc label translate-alist)
217 (error "Duplicate label %s" label))
218 (setq new-label (concat (match-string 1 (car entry))
219 (int-to-string (incf (cdr nr-cell)))))
220 (push (cons label new-label) translate-alist)
221 (or (string= label new-label) (setq changed-sequence t))))
1a9461d0
CD
222
223 (unless changed-sequence
224 (error "Simple labels are already in correct sequence"))
225
d8fb2015
CD
226 (reftex-ensure-write-access (reftex-all-document-files))
227
1a9461d0
CD
228 ;; Save all document buffers before this operation
229 (reftex-save-all-document-buffers)
230
4c36be58 231 ;; First test to check for errors.
09e80d9f 232 (setq n (reftex-translate
3666daf6 233 files search-re translate-alist error-fmt 'test))
1a9461d0
CD
234
235 ;; Now the real thing.
09e80d9f 236 (if (yes-or-no-p
3666daf6
CD
237 (format "Replace %d items at %d places in %d files? "
238 (length translate-alist) n (length files)))
239 (progn
240 (let ((inhibit-quit t)) ;; Do not disturb...
241 (reftex-translate
242 files search-re translate-alist error-fmt nil)
243 (setq quit-flag nil))
244 (if (and (reftex-is-multi)
245 (yes-or-no-p "Save entire document? "))
246 (reftex-save-all-document-buffers))
247 ;; Rescan again...
248 (reftex-access-scan-info 1)
249 (message "Done replacing simple labels."))
1a9461d0
CD
250 (message "No replacements done"))))
251
252(defun reftex-translate (files search-re translate-alist error-fmt test)
253 ;; In FILES, look for SEARCH-RE and replace match 1 of it with
0b381c7e 254 ;; its association in TRANSLATE-ALIST.
1a9461d0 255 ;; If we do not find an association and TEST is non-nil, query
09e80d9f 256 ;; to ignore the problematic string.
1a9461d0
CD
257 ;; If TEST is nil, it is ignored without query.
258 ;; Return the number of replacements.
259 (let ((n 0) file label match-data buf macro pos cell)
260 (while (setq file (pop files))
261 (setq buf (reftex-get-file-buffer-force file))
262 (unless buf
3666daf6 263 (error "No such file %s" file))
1a9461d0
CD
264 (set-buffer buf)
265 (save-excursion
3666daf6
CD
266 (save-restriction
267 (widen)
268 (goto-char (point-min))
269 (while (re-search-forward search-re nil t)
270 (backward-char)
271 (save-excursion
272 (setq label (reftex-match-string 1)
273 cell (assoc label translate-alist)
274 match-data (match-data)
275 macro (reftex-what-macro 1)
276 pos (cdr macro))
277 (goto-char (or pos (point)))
278 (when (and macro
279 (or (looking-at "\\\\ref")
280 (looking-at "\\\\[a-zA-Z]*ref\\(range\\)?[^a-zA-Z]")
281 (looking-at "\\\\ref[a-zA-Z]*[^a-zA-Z]")
09e80d9f 282 (looking-at (format
3666daf6
CD
283 reftex-find-label-regexp-format
284 (regexp-quote label)))))
285 ;; OK, we should replace it.
286 (set-match-data match-data)
287 (cond
288 ((and test (not cell))
289 ;; We've got a problem
290 (unwind-protect
291 (progn
292 (reftex-highlight 1 (match-beginning 0) (match-end 0))
293 (ding)
294 (or (y-or-n-p (format error-fmt label))
295 (error "Abort")))
296 (reftex-unhighlight 1)))
297 ((and test cell)
298 (incf n))
299 ((and (not test) cell)
300 ;; Replace
301 (goto-char (match-beginning 1))
302 (delete-region (match-beginning 1) (match-end 1))
303 (insert (cdr cell)))
304 (t nil))))))))
1a9461d0
CD
305 n))
306
307(defun reftex-save-all-document-buffers ()
308 "Save all documents associated with the current document.
309The function is useful after a global action like replacing or renumbering
310labels."
311 (interactive)
312 (let ((files (reftex-all-document-files))
3666daf6 313 file buffer)
9a529312 314 (save-current-buffer
1a9461d0 315 (while (setq file (pop files))
3666daf6
CD
316 (setq buffer (reftex-get-buffer-visiting file))
317 (when buffer
318 (set-buffer buffer)
319 (save-buffer))))))
1a9461d0 320
d8fb2015
CD
321(defun reftex-ensure-write-access (files)
322 "Make sure we have write access to all files in FILES.
323Also checks if buffers visiting the files are in read-only mode."
324 (let (file buf)
325 (while (setq file (pop files))
326 (unless (file-exists-p file)
3666daf6
CD
327 (ding)
328 (or (y-or-n-p (format "No such file %s. Continue? " file))
329 (error "Abort")))
d8fb2015 330 (unless (file-writable-p file)
3666daf6
CD
331 (ding)
332 (or (y-or-n-p (format "No write access to %s. Continue? " file))
333 (error "Abort")))
d8fb2015 334 (when (and (setq buf (reftex-get-buffer-visiting file))
9a529312 335 (with-current-buffer buf
3666daf6
CD
336 buffer-read-only))
337 (ding)
338 (or (y-or-n-p (format "Buffer %s is read-only. Continue? "
339 (buffer-name buf)))
340 (error "Abort"))))))
1a9461d0 341
fa862320
JL
342;;; Multi-file RefTeX Isearch
343
9a529312
SM
344;; `reftex-isearch-wrap-function', `reftex-isearch-push-state-function',
345;; `reftex-isearch-pop-state-function', `reftex-isearch-isearch-search'
346;; functions remain here only for backward-compatibility with Emacs 22
347;; and are obsolete since Emacs 23 that supports a single function
348;; variable `multi-isearch-next-buffer-function'.
fa862320 349
0072e19e 350(defun reftex-isearch-wrap-function ()
8cbd80f7
JL
351 (switch-to-buffer
352 (funcall isearch-next-buffer-function (current-buffer) t))
0072e19e
CD
353 (goto-char (if isearch-forward (point-min) (point-max))))
354
355(defun reftex-isearch-push-state-function ()
356 `(lambda (cmd)
357 (reftex-isearch-pop-state-function cmd ,(current-buffer))))
358
359(defun reftex-isearch-pop-state-function (cmd buffer)
360 (switch-to-buffer buffer))
361
362(defun reftex-isearch-isearch-search (string bound noerror)
363 (let ((nxt-buff nil)
8cbd80f7 364 (search-fun (isearch-search-fun-default)))
0072e19e
CD
365 (or
366 (funcall search-fun string bound noerror)
367 (unless bound
368 (condition-case nil
369 (when isearch-next-buffer-function
370 (while (not (funcall search-fun string bound noerror))
371 (cond
372 (isearch-forward
373 (setq nxt-buff
374 (funcall isearch-next-buffer-function
375 (current-buffer)))
376 (if (not nxt-buff)
377 (progn
378 (error "Wrap forward"))
379 (switch-to-buffer nxt-buff)
380 (goto-char (point-min))))
381 (t
382 (setq nxt-buff
383 (funcall isearch-next-buffer-function
384 (current-buffer)))
385 (if (not nxt-buff)
386 (progn
387 (error "Wrap backward"))
388 (switch-to-buffer nxt-buff)
389 (goto-char (point-max))))))
390 (point))
391 (error nil))))))
392
9a529312
SM
393;; This function is called when isearch reaches the end of a
394;; buffer. For reftex what we want to do is not wrap to the
395;; beginning, but switch to the next buffer in the logical order of
396;; the document. This function looks through list of files in the
397;; document (reftex-all-document-files), searches for the current
398;; buffer and switches to the next/previous one in the logical order
399;; of the document. If WRAPP is true then wrap the search to the
400;; beginning/end of the file list, depending of the search direction.
0072e19e
CD
401(defun reftex-isearch-switch-to-next-file (crt-buf &optional wrapp)
402 (reftex-access-scan-info)
b576c580
GM
403 (let ((cb (buffer-file-name crt-buf))
404 (flist (reftex-all-document-files)))
0072e19e
CD
405 (when flist
406 (if wrapp
407 (unless isearch-forward
4f595e15 408 (setq flist (last flist)))
0072e19e 409 (unless isearch-forward
b576c580 410 (setq flist (reverse flist)))
0072e19e
CD
411 (while (not (string= (car flist) cb))
412 (setq flist (cdr flist)))
413 (setq flist (cdr flist)))
414 (when flist
fa862320 415 (find-file-noselect (car flist))))))
0072e19e 416
0072e19e
CD
417;;;###autoload
418(defun reftex-isearch-minor-mode (&optional arg)
419 "When on, isearch searches the whole document, not only the current file.
420This minor mode allows isearch to search through all the files of
421the current TeX document.
422
423With no argument, this command toggles
424`reftex-isearch-minor-mode'. With a prefix argument ARG, turn
3ecd3a56 425`reftex-isearch-minor-mode' on if ARG is positive, otherwise turn it off."
0072e19e
CD
426 (interactive "P")
427 (let ((old-reftex-isearch-minor-mode reftex-isearch-minor-mode))
09e80d9f 428 (setq reftex-isearch-minor-mode
0072e19e
CD
429 (not (or (and (null arg) reftex-isearch-minor-mode)
430 (<= (prefix-numeric-value arg) 0))))
431 (unless (eq reftex-isearch-minor-mode old-reftex-isearch-minor-mode)
432 (if reftex-isearch-minor-mode
433 (progn
434 (dolist (crt-buf (buffer-list))
435 (with-current-buffer crt-buf
436 (when reftex-mode
fa862320 437 (if (boundp 'multi-isearch-next-buffer-function)
4f595e15
RA
438 (set (make-local-variable
439 'multi-isearch-next-buffer-function)
fa862320
JL
440 'reftex-isearch-switch-to-next-file)
441 (set (make-local-variable 'isearch-wrap-function)
442 'reftex-isearch-wrap-function)
443 (set (make-local-variable 'isearch-search-fun-function)
444 (lambda () 'reftex-isearch-isearch-search))
445 (set (make-local-variable 'isearch-push-state-function)
446 'reftex-isearch-push-state-function)
447 (set (make-local-variable 'isearch-next-buffer-function)
448 'reftex-isearch-switch-to-next-file))
0072e19e
CD
449 (setq reftex-isearch-minor-mode t))))
450 (add-hook 'reftex-mode-hook 'reftex-isearch-minor-mode))
451 (dolist (crt-buf (buffer-list))
452 (with-current-buffer crt-buf
453 (when reftex-mode
fa862320
JL
454 (if (boundp 'multi-isearch-next-buffer-function)
455 (kill-local-variable 'multi-isearch-next-buffer-function)
456 (kill-local-variable 'isearch-wrap-function)
457 (kill-local-variable 'isearch-search-fun-function)
458 (kill-local-variable 'isearch-push-state-function)
459 (kill-local-variable 'isearch-next-buffer-function))
0072e19e
CD
460 (setq reftex-isearch-minor-mode nil))))
461 (remove-hook 'reftex-mode-hook 'reftex-isearch-minor-mode)))
37269466 462 ;; Force mode line redisplay.
0072e19e
CD
463 (set-buffer-modified-p (buffer-modified-p))))
464
09e80d9f 465(add-minor-mode 'reftex-isearch-minor-mode "/I" nil nil
0072e19e
CD
466 'reftex-isearch-minor-mode)
467
1a9461d0 468;;; reftex-global.el ends here