25280
|
1 ;;; reftex-global.el - Operations on entire documents with RefTeX
|
|
2 ;;; Version: 4.5
|
|
3 ;;;
|
|
4 ;;; See main file reftex.el for licensing information
|
|
5
|
|
6 (provide 'reftex-global)
|
|
7 (require 'reftex)
|
|
8 ;;;
|
|
9
|
|
10 (defun reftex-create-tags-file ()
|
|
11 "Create TAGS file by running `etags' on the current document.
|
|
12 The TAGS file is also immediately visited with `visit-tags-table'."
|
|
13 (interactive)
|
|
14 (reftex-access-scan-info current-prefix-arg)
|
|
15 (let* ((master (reftex-TeX-master-file))
|
|
16 (files (reftex-all-document-files))
|
|
17 (cmd (format "etags %s" (mapconcat 'identity files " "))))
|
|
18 (save-excursion
|
|
19 (set-buffer (reftex-get-buffer-visiting master))
|
|
20 (message "Running etags to create TAGS file...")
|
|
21 (shell-command cmd)
|
|
22 (visit-tags-table "TAGS"))))
|
|
23
|
|
24 ;; History of grep commands.
|
|
25 (defvar reftex-grep-history nil)
|
|
26 (defvar reftex-grep-command "grep -n "
|
|
27 "Last grep command used in \\[reftex-grep-document]; default for next grep.")
|
|
28
|
|
29 (defun reftex-grep-document (grep-cmd)
|
|
30 "Run grep query through all files related to this document.
|
|
31 With prefix arg, force to rescan document.
|
|
32 No active TAGS table is required."
|
|
33
|
|
34 (interactive
|
|
35 (list (read-from-minibuffer "Run grep on document (like this): "
|
|
36 reftex-grep-command nil nil
|
|
37 'reftex-grep-history)))
|
|
38 (reftex-access-scan-info current-prefix-arg)
|
|
39 (let* ((files (reftex-all-document-files t))
|
|
40 (cmd (format
|
|
41 "%s %s" grep-cmd
|
|
42 (mapconcat 'identity files " "))))
|
|
43 (grep cmd)))
|
|
44
|
|
45 (defun reftex-search-document (&optional regexp)
|
|
46 "Regexp search through all files of the current document.
|
|
47 Starts always in the master file. Stops when a match is found.
|
|
48 To continue searching for next match, use command \\[tags-loop-continue].
|
|
49 No active TAGS table is required."
|
|
50 (interactive)
|
|
51 (let ((default (reftex-this-word)))
|
|
52 (unless regexp
|
|
53 (setq regexp (read-string (format "Search regexp in document [%s]: "
|
|
54 default))))
|
|
55 (if (string= regexp "") (setq regexp (regexp-quote default)))
|
|
56
|
|
57 (reftex-access-scan-info current-prefix-arg)
|
|
58 (tags-search regexp (list 'reftex-all-document-files))))
|
|
59
|
|
60 (defun reftex-query-replace-document (&optional from to delimited)
|
|
61 "Run a query-replace-regexp of FROM with TO over the entire document.
|
|
62 Third arg DELIMITED (prefix arg) means replace only word-delimited matches.
|
|
63 If you exit (\\[keyboard-quit] or ESC), you can resume the query replace
|
|
64 with the command \\[tags-loop-continue].
|
|
65 No active TAGS table is required."
|
|
66 (interactive)
|
|
67 (let ((default (reftex-this-word)))
|
|
68 (unless from
|
|
69 (setq from (read-string (format "Replace regexp in document [%s]: "
|
|
70 default)))
|
|
71 (if (string= from "") (setq from (regexp-quote default))))
|
|
72 (unless to
|
|
73 (setq to (read-string (format "Replace regexp %s with: " from))))
|
|
74 (reftex-access-scan-info current-prefix-arg)
|
|
75 (tags-query-replace from to (or delimited current-prefix-arg)
|
|
76 (list 'reftex-all-document-files))))
|
|
77
|
|
78 (defun reftex-find-duplicate-labels ()
|
|
79 "Produce a list of all duplicate labels in the document."
|
|
80
|
|
81 (interactive)
|
|
82
|
|
83 ;; Rescan the document to make sure
|
|
84 (reftex-access-scan-info t)
|
|
85
|
|
86 (let ((master (reftex-TeX-master-file))
|
|
87 (cnt 0)
|
|
88 (dlist
|
|
89 (mapcar
|
|
90 (lambda (x)
|
|
91 (let (x1)
|
|
92 (cond
|
|
93 ((memq (car x)
|
|
94 '(toc bof eof bib thebib label-numbers xr xr-doc
|
|
95 master-dir file-error bibview-cache appendix
|
|
96 is-multi index))
|
|
97 nil)
|
|
98 (t
|
|
99 (setq x1 (reftex-all-assoc-string
|
|
100 (car x) (symbol-value reftex-docstruct-symbol)))
|
|
101 (if (< 1 (length x1))
|
|
102 (append (list (car x))
|
|
103 (mapcar (lambda(x)
|
|
104 (abbreviate-file-name (nth 3 x)))
|
|
105 x1))
|
|
106 (list nil))))))
|
|
107 (reftex-uniquify-by-car (symbol-value reftex-docstruct-symbol)))))
|
|
108
|
|
109 (setq dlist (reftex-uniquify-by-car dlist))
|
|
110 (if (null dlist) (error "No duplicate labels in document"))
|
|
111 (switch-to-buffer-other-window "*Duplicate Labels*")
|
|
112 (set (make-local-variable 'TeX-master) master)
|
|
113 (erase-buffer)
|
|
114 (insert " MULTIPLE LABELS IN CURRENT DOCUMENT:\n")
|
|
115 (insert
|
|
116 " Move point to label and type `r' to run a query-replace on the label\n"
|
|
117 " and its references. Type `q' to exit this buffer.\n\n")
|
|
118 (insert " LABEL FILE\n")
|
|
119 (insert " -------------------------------------------------------------\n")
|
|
120 (use-local-map (make-sparse-keymap))
|
|
121 (local-set-key [?q] (lambda () "Kill this buffer." (interactive)
|
|
122 (kill-buffer (current-buffer)) (delete-window)))
|
|
123 (local-set-key [?r] 'reftex-change-label)
|
|
124 (while dlist
|
|
125 (when (and (car (car dlist))
|
|
126 (cdr (car dlist)))
|
|
127 (incf cnt)
|
|
128 (insert (mapconcat 'identity (car dlist) "\n ") "\n"))
|
|
129 (pop dlist))
|
|
130 (goto-char (point-min))
|
|
131 (when (= cnt 0)
|
|
132 (kill-buffer (current-buffer))
|
|
133 (delete-window)
|
|
134 (message "Document does not contain duplicate labels."))))
|
|
135
|
|
136 (defun reftex-change-label (&optional from to)
|
|
137 "Query replace FROM with TO in all \\label and \\ref commands.
|
|
138 Works on the entire multifile document.
|
|
139 If you exit (\\[keyboard-quit] or ESC), you can resume the query replace
|
|
140 with the command \\[tags-loop-continue].
|
|
141 No active TAGS table is required."
|
|
142 (interactive)
|
|
143 (let ((default (reftex-this-word "-a-zA-Z0-9_*.:")))
|
|
144 (unless from
|
|
145 (setq from (read-string (format "Replace label globally [%s]: "
|
|
146 default))))
|
|
147 (if (string= from "") (setq from default))
|
|
148 (unless to
|
|
149 (setq to (read-string (format "Replace label %s with: "
|
|
150 from))))
|
|
151 (reftex-query-replace-document
|
|
152 (concat "\\\\\\(label\\|[a-z]*ref\\){" (regexp-quote from) "}")
|
|
153 (format "\\\\\\1{%s}" to))))
|
|
154
|
|
155 (defun reftex-renumber-simple-labels ()
|
|
156 "Renumber all simple labels in the document to make them sequentially.
|
|
157 Simple labels are the ones created by RefTeX, consisting only of the
|
|
158 prefix and a number. After the command completes, all these labels will
|
|
159 have sequential numbers throughout the document. Any references to
|
|
160 the labels will be changed as well. For this, RefTeX looks at the
|
|
161 arguments of any macros which either start or end in the string `ref'.
|
|
162 This command should be used with care, in particular in multifile
|
|
163 documents. You should not use it if another document refers to this
|
|
164 one with the `xr' package."
|
|
165 (interactive)
|
|
166 ;; Resan the entire document
|
|
167 (reftex-access-scan-info 1)
|
|
168 ;; Get some insurance
|
|
169 (if (and (reftex-is-multi)
|
|
170 (not (yes-or-no-p "Replacing all simple labels in multiple files is risky. Continue? ")))
|
|
171 (error "Abort"))
|
|
172 ;; Make the translation list
|
|
173 (let* ((re-core (concat "\\("
|
|
174 (mapconcat 'cdr reftex-typekey-to-prefix-alist "\\|")
|
|
175 "\\)"))
|
|
176 (label-re (concat "\\`" re-core "\\([0-9]+\\)\\'"))
|
|
177 (search-re (concat "[{,]\\(" re-core "\\([0-9]+\\)\\)[,}]"))
|
|
178 (error-fmt "Undefined label or reference %s. Ignore and continue? ")
|
|
179 (label-numbers-alist (mapcar (lambda (x) (cons (cdr x) 0))
|
|
180 reftex-typekey-to-prefix-alist))
|
|
181 (files (reftex-all-document-files))
|
|
182 (list (symbol-value reftex-docstruct-symbol))
|
|
183 translate-alist n entry label new-label nr-cell changed-sequence)
|
|
184
|
|
185 (while (setq entry (pop list))
|
|
186 (when (and (stringp (car entry))
|
|
187 (string-match label-re (car entry)))
|
|
188 (setq label (car entry)
|
|
189 nr-cell (assoc (match-string 1 (car entry))
|
|
190 label-numbers-alist))
|
|
191 (if (assoc label translate-alist)
|
|
192 (error "Duplicate label %s" label))
|
|
193 (setq new-label (concat (match-string 1 (car entry))
|
|
194 (incf (cdr nr-cell))))
|
|
195 (push (cons label new-label) translate-alist)
|
|
196 (or (string= label new-label) (setq changed-sequence t))))
|
|
197
|
|
198 (unless changed-sequence
|
|
199 (error "Simple labels are already in correct sequence"))
|
|
200
|
|
201 ;; Save all document buffers before this operation
|
|
202 (reftex-save-all-document-buffers)
|
|
203
|
|
204 ;; First test to check for erros
|
|
205 (setq n (reftex-translate
|
|
206 files search-re translate-alist error-fmt 'test))
|
|
207
|
|
208 ;; Now the real thing.
|
|
209 (if (yes-or-no-p
|
|
210 (format "Replace %d items at %d places in %d files? "
|
|
211 (length translate-alist) n (length files)))
|
|
212 (progn
|
|
213 (let ((inhibit-quit t)) ;; Do not disturb...
|
|
214 (reftex-translate
|
|
215 files search-re translate-alist error-fmt nil)
|
|
216 (setq quit-flag nil))
|
|
217 (if (and (reftex-is-multi)
|
|
218 (yes-or-no-p "Save entire document? "))
|
|
219 (reftex-save-all-document-buffers))
|
|
220 ;; Rescan again...
|
|
221 (reftex-access-scan-info 1)
|
|
222 (message "Done replacing simple labels."))
|
|
223 (message "No replacements done"))))
|
|
224
|
|
225 (defun reftex-translate (files search-re translate-alist error-fmt test)
|
|
226 ;; In FILES, look for SEARCH-RE and replace match 1 of it with
|
|
227 ;; its association in TRANSLATE-ALSIT.
|
|
228 ;; If we do not find an association and TEST is non-nil, query
|
|
229 ;; to ignore the problematic string.
|
|
230 ;; If TEST is nil, it is ignored without query.
|
|
231 ;; Return the number of replacements.
|
|
232 (let ((n 0) file label match-data buf macro pos cell)
|
|
233 (while (setq file (pop files))
|
|
234 (setq buf (reftex-get-file-buffer-force file))
|
|
235 (unless buf
|
|
236 (error "No such file %s" file))
|
|
237 (set-buffer buf)
|
|
238 (save-excursion
|
|
239 (save-restriction
|
|
240 (widen)
|
|
241 (goto-char (point-min))
|
|
242 (while (re-search-forward search-re nil t)
|
|
243 (backward-char)
|
|
244 (save-excursion
|
|
245 (setq label (reftex-match-string 1)
|
|
246 cell (assoc label translate-alist)
|
|
247 match-data (match-data)
|
|
248 macro (reftex-what-macro 1)
|
|
249 pos (cdr macro))
|
|
250 (goto-char (or pos (point)))
|
|
251 (when (and macro
|
|
252 (or (looking-at "\\\\ref")
|
|
253 (looking-at "\\\\[a-zA-Z]*ref\\(range\\)?[^a-zA-Z]")
|
|
254 (looking-at "\\\\ref[a-zA-Z]*[^a-zA-Z]")
|
|
255 (looking-at (format
|
|
256 reftex-find-label-regexp-format
|
|
257 (regexp-quote label)))))
|
|
258 ;; OK, we should replace it.
|
|
259 (set-match-data match-data)
|
|
260 (cond
|
|
261 ((and test (not cell))
|
|
262 ;; We've got a problem
|
|
263 (unwind-protect
|
|
264 (progn
|
|
265 (reftex-highlight 1 (match-beginning 0) (match-end 0))
|
|
266 (ding)
|
|
267 (or (y-or-n-p (format error-fmt label))
|
|
268 (error "Abort")))
|
|
269 (reftex-unhighlight 1)))
|
|
270 ((and test cell)
|
|
271 (incf n))
|
|
272 ((and (not test) cell)
|
|
273 ;; Replace
|
|
274 (goto-char (match-beginning 1))
|
|
275 (delete-region (match-beginning 1) (match-end 1))
|
|
276 (insert (cdr cell)))
|
|
277 (t nil))))))))
|
|
278 n))
|
|
279
|
|
280 (defun reftex-save-all-document-buffers ()
|
|
281 "Save all documents associated with the current document.
|
|
282 The function is useful after a global action like replacing or renumbering
|
|
283 labels."
|
|
284 (interactive)
|
|
285 (let ((files (reftex-all-document-files))
|
|
286 file buffer)
|
|
287 (save-excursion
|
|
288 (while (setq file (pop files))
|
|
289 (setq buffer (reftex-get-buffer-visiting file))
|
|
290 (when buffer
|
|
291 (set-buffer buffer)
|
|
292 (save-buffer))))))
|
|
293
|
|
294
|
|
295 ;;; reftex-global.el ends here
|