Initial revision
[bpt/emacs.git] / lisp / textmodes / reftex-vcr.el
CommitLineData
1a9461d0
CD
1;;; reftex-vcr.el - Viewing cross references and citations with RefTeX
2;;; Version: 4.5
3;;;
4;;; See main file reftex.el for licensing information
5
6(provide 'reftex-vcr)
7(require 'reftex)
8;;;
9
10(defun reftex-view-crossref (&optional arg auto-how)
11 "View cross reference of macro at point. Point must be on the KEY
12argument. When at at `\ref' macro, show corresponding `\label'
13definition, also in external documents (`xr'). When on a label, show
14a locations where KEY is referenced. Subsequent calls find additional
15locations. When on a `\cite', show the associated `\bibitem' macro or
16the BibTeX database entry. When on a `\bibitem', show a `\cite' macro
17which uses this KEY. When on an `\index', show other locations marked
18by the same index entry.
19To define additional cross referencing items, use the option
20`reftex-view-crossref-extra'. See also `reftex-view-crossref-from-bibtex'.
21With one or two C-u prefixes, enforce rescanning of the document.
22With argument 2, select the window showing the cross reference.
23AUTO-HOW is only for the automatic crossref display and is handed through
24to the functions `reftex-view-cr-cite' and `reftex-view-cr-ref'."
25
26 (interactive "P")
27 ;; See where we are.
28 (let* ((macro (car (reftex-what-macro-safe 1)))
29 (key (reftex-this-word "^{}%\n\r,"))
30 dw)
31
32 (if (or (null macro) (reftex-in-comment))
33 (error "Not on a crossref macro argument"))
34
35 (setq reftex-call-back-to-this-buffer (current-buffer))
36
37 (cond
38 ((string-match "\\`\\\\cite\\|cite\\*?\\'" macro)
39 ;; A citation macro: search for bibitems or BibTeX entries
40 (setq dw (reftex-view-cr-cite arg key auto-how)))
41 ((string-match "\\`\\\\ref\\|ref\\(range\\)?\\*?\\'" macro)
42 ;; A reference macro: search for labels
43 (setq dw (reftex-view-cr-ref arg key auto-how)))
44 (auto-how nil) ;; No further action for automatic display (speed)
45 ((or (equal macro "\\label")
46 (member macro reftex-macros-with-labels))
47 ;; A label macro: search for reference macros
48 (reftex-access-scan-info arg)
49 (setq dw (reftex-view-regexp-match
50 (format reftex-find-reference-format (regexp-quote key))
51 4 nil nil)))
52 ((equal macro "\\bibitem")
53 ;; A bibitem macro: search for citations
54 (reftex-access-scan-info arg)
55 (setq dw (reftex-view-regexp-match
56 (format reftex-find-citation-regexp-format (regexp-quote key))
57 3 nil nil)))
58 ((member macro reftex-macros-with-index)
59 (reftex-access-scan-info arg)
60 (setq dw (reftex-view-regexp-match
61 (format reftex-find-index-entry-regexp-format
62 (regexp-quote key))
63 3 nil nil)))
64 (t
65 (reftex-access-scan-info arg)
66 (catch 'exit
67 (let ((list reftex-view-crossref-extra)
68 entry mre action group)
69 (while (setq entry (pop list))
70 (setq mre (car entry)
71 action (nth 1 entry)
72 group (nth 2 entry))
73 (when (string-match mre macro)
74 (setq dw (reftex-view-regexp-match
75 (format action key) group nil nil))
76 (throw 'exit t))))
77 (error "Not on a crossref macro argument"))))
78 (if (and (eq arg 2) (windowp dw)) (select-window dw))))
79
80(defun reftex-view-cr-cite (arg key how)
81 ;; View crossreference of a ref cite. HOW can have the values
82 ;; nil: Show in another window.
83 ;; echo: Show one-line info in echo area.
84 ;; tmp-window: Show in small window and arrange for window to disappear.
85
86 ;; Ensure access to scanning info
87 (reftex-access-scan-info (or arg current-prefix-arg))
88
89 (if (eq how 'tmp-window)
90 ;; Remember the window configuration
91 (put 'reftex-auto-view-crossref 'last-window-conf
92 (current-window-configuration)))
93
94 (let (files size item (pos (point)) (win (selected-window)) pop-win)
95 ;; Find the citation mode and the file list
96 (cond
97 ((assq 'bib (symbol-value reftex-docstruct-symbol))
98 (setq item nil
99 files (reftex-get-bibfile-list)))
100 ((assq 'thebib (symbol-value reftex-docstruct-symbol))
101 (setq item t
102 files (list (cdr (assq 'thebib
103 (symbol-value reftex-docstruct-symbol))))))
104 (reftex-default-bibliography
105 (setq item nil
106 files (reftex-default-bibliography)))
107 (how) ;; don't throw for special display
108 (t (error "Cannot display crossref")))
109
110 (if (eq how 'echo)
111 ;; Display in Echo area
112 (reftex-echo-cite key files item)
113 ;; Display in a window
114 (if (not (eq how 'tmp-window))
115 ;; Normal display
116 (reftex-pop-to-bibtex-entry key files nil t item)
117 ;; A temporary window
118 (condition-case nil
119 (reftex-pop-to-bibtex-entry key files nil t item)
120 (error (goto-char pos)
121 (message "cite: no such citation key %s" key)
122 (error "")))
123 ;; Resize the window
124 (setq size (max 1 (count-lines (point)
125 (reftex-end-of-bib-entry item))))
126 (let ((window-min-height 2))
127 (shrink-window (1- (- (window-height) size)))
128 (recenter 0))
129 ;; Arrange restoration
130 (add-hook 'pre-command-hook 'reftex-restore-window-conf))
131
132 ;; Normal display in other window
133 (add-hook 'pre-command-hook 'reftex-highlight-shall-die)
134 (setq pop-win (selected-window))
135 (select-window win)
136 (goto-char pos)
137 (when (equal arg 2)
138 (select-window pop-win)))))
139
140(defun reftex-view-cr-ref (arg label how)
141 ;; View crossreference of a ref macro. HOW can have the values
142 ;; nil: Show in another window.
143 ;; echo: Show one-line info in echo area.
144 ;; tmp-window: Show in small window and arrange for window to disappear.
145
146 ;; Ensure access to scanning info
147 (reftex-access-scan-info (or arg current-prefix-arg))
148
149 (if (eq how 'tmp-window)
150 ;; Remember the window configuration
151 (put 'reftex-auto-view-crossref 'last-window-conf
152 (current-window-configuration)))
153
154 (let* ((xr-data (assoc 'xr (symbol-value reftex-docstruct-symbol)))
155 (xr-re (nth 2 xr-data))
156 (entry (assoc label (symbol-value reftex-docstruct-symbol)))
157 (win (selected-window)) pop-win (pos (point)))
158
159 (if (and (not entry) (stringp label) xr-re (string-match xr-re label))
160 ;; Label is defined in external document
161 (save-excursion
162 (save-match-data
163 (set-buffer
164 (or (reftex-get-file-buffer-force
165 (cdr (assoc (match-string 1 label) (nth 1
166 xr-data))))
167 (error "Problem with external label %s" label))))
168 (setq label (substring label (match-end 1)))
169 (reftex-access-scan-info)
170 (setq entry
171 (assoc label (symbol-value reftex-docstruct-symbol)))))
172 (if (eq how 'echo)
173 ;; Display in echo area
174 (reftex-echo-ref label entry (symbol-value reftex-docstruct-symbol))
175 (let ((window-conf (current-window-configuration)))
176 (condition-case nil
177 (reftex-show-label-location entry t nil t t)
178 (error (set-window-configuration window-conf)
179 (message "ref: Label %s not found" label)
180 (error "ref: Label %s not found" label)))) ;; 2nd is line OK
181 (add-hook 'pre-command-hook 'reftex-highlight-shall-die)
182
183 (when (eq how 'tmp-window)
184 ;; Resize window and arrange restauration
185 (shrink-window (1- (- (window-height) 9)))
186 (recenter '(4))
187 (add-hook 'pre-command-hook 'reftex-restore-window-conf))
188 (setq pop-win (selected-window))
189 (select-window win)
190 (goto-char pos)
191 (when (equal arg 2)
192 (select-window pop-win)))))
193
194(defun reftex-mouse-view-crossref (ev)
195 "View cross reference of \\ref or \\cite macro where you click.
196If the macro at point is a \\ref, show the corresponding label definition.
197If it is a \\cite, show the BibTeX database entry.
198If there is no such macro at point, search forward to find one.
199With argument, actually select the window showing the cross reference."
200 (interactive "e")
201 (mouse-set-point ev)
202 (reftex-view-crossref current-prefix-arg))
203
204(defun reftex-view-crossref-when-idle ()
205 ;; Display info about crossref at point in echo area or a window.
206 ;; This function was desigend to work with an idle timer.
207 ;; We try to get out of here as quickly as possible if the call is useless.
208 (and reftex-mode
209 ;; Make sure message area is free if we need it.
210 (or (eq reftex-auto-view-crossref 'window) (not (current-message)))
211 ;; Make sure we are not already displaying this one
212 (not (memq last-command '(reftex-view-crossref
213 reftex-mouse-view-crossref)))
214 ;; Quick precheck if this might be a relevant spot
215 ;; FIXME: Can fail with backslash in comment
216 (save-excursion
217 (search-backward "\\" nil t)
218 (looking-at "\\\\[a-zA-Z]*\\(cite\\|ref\\)"))
219
220 (condition-case nil
221 (let ((current-prefix-arg nil))
222 (cond
223 ((eq reftex-auto-view-crossref t)
224 (reftex-view-crossref -1 'echo))
225 ((eq reftex-auto-view-crossref 'window)
226 (reftex-view-crossref -1 'tmp-window))
227 (t nil)))
228 (error nil))))
229
230(defun reftex-restore-window-conf ()
231 (set-window-configuration (get 'reftex-auto-view-crossref 'last-window-conf))
232 (put 'reftex-auto-view-crossref 'last-window-conf nil)
233 (remove-hook 'pre-command-hook 'reftex-restore-window-conf))
234
235(defun reftex-echo-ref (label entry docstruct)
236 ;; Display crossref info in echo area.
237 (cond
238 ((null docstruct)
239 (message (substitute-command-keys (format reftex-no-info-message "ref"))))
240 ((null entry)
241 (message "ref: unknown label: %s" label))
242 (t
243 (when (stringp (nth 2 entry))
244 (message "ref(%s): %s" (nth 1 entry) (nth 2 entry)))
245 (let ((buf (get-buffer " *Echo Area*")))
246 (when buf
247 (save-excursion
248 (set-buffer buf)
249 (run-hooks 'reftex-display-copied-context-hook)))))))
250
251(defun reftex-echo-cite (key files item)
252 ;; Display citation info in echo area.
253 (let* ((cache (assq 'bibview-cache (symbol-value reftex-docstruct-symbol)))
254 (cache-entry (assoc key (cdr cache)))
255 entry string buf (all-files files))
256
257 (if (and reftex-cache-cite-echo cache-entry)
258 ;; We can just use the cache
259 (setq string (cdr cache-entry))
260
261 ;; Need to look in the database
262 (unless reftex-revisit-to-echo
263 (setq files (reftex-visited-files files)))
264
265 (setq entry
266 (condition-case nil
267 (save-excursion
268 (reftex-pop-to-bibtex-entry key files nil nil item t))
269 (error
270 (if (and files (= (length all-files) (length files)))
271 (message "cite: no such database entry: %s" key)
272 (message (substitute-command-keys
273 (format reftex-no-info-message "cite"))))
274 nil)))
275 (when entry
276 (if item
277 (setq string (reftex-nicify-text entry))
278 (setq string (reftex-make-cite-echo-string
279 (reftex-parse-bibtex-entry entry)
280 reftex-docstruct-symbol)))))
281 (unless (or (null string) (equal string ""))
282 (message "cite: %s" string))
283 (when (setq buf (get-buffer " *Echo Area*"))
284 (save-excursion
285 (set-buffer buf)
286 (run-hooks 'reftex-display-copied-context-hook)))))
287
288(defvar reftex-use-itimer-in-xemacs nil
289 "*Non-nil means use the idle timers in XEmacs for crossref display.
290Currently, idle timer restart is broken and we use the post-command-hook.")
291
292(defun reftex-toggle-auto-view-crossref ()
293 "Toggle the automatic display of crossref information in the echo area.
294When active, leaving point idle in the argument of a \\ref or \\cite macro
295will display info in the echo area."
296 (interactive)
297 (if reftex-auto-view-crossref-timer
298 (progn
299 (if (featurep 'xemacs)
300 (if reftex-use-itimer-in-xemacs
301 (delete-itimer reftex-auto-view-crossref-timer)
302 (remove-hook 'post-command-hook 'reftex-start-itimer-once))
303 (cancel-timer reftex-auto-view-crossref-timer))
304 (setq reftex-auto-view-crossref-timer nil)
305 (message "Automatic display of crossref information was turned off"))
306 (setq reftex-auto-view-crossref-timer
307 (if (featurep 'xemacs)
308 (if reftex-use-itimer-in-xemacs
309 (start-itimer "RefTeX Idle Timer"
310 'reftex-view-crossref-when-idle
311 reftex-idle-time reftex-idle-time t)
312 (add-hook 'post-command-hook 'reftex-start-itimer-once)
313 t)
314 (run-with-idle-timer
315 reftex-idle-time t 'reftex-view-crossref-when-idle)))
316 (unless reftex-auto-view-crossref
317 (setq reftex-auto-view-crossref t))
318 (message "Automatic display of crossref information was turned on")))
319
320(defun reftex-start-itimer-once ()
321 (and reftex-mode
322 (not (itimer-live-p reftex-auto-view-crossref-timer))
323 (setq reftex-auto-view-crossref-timer
324 (start-itimer "RefTeX Idle Timer"
325 'reftex-view-crossref-when-idle
326 reftex-idle-time nil t))))
327
328(defun reftex-view-crossref-from-bibtex (&optional arg)
329 "View location in a LaTeX document which cites the BibTeX entry at point.
330Since BibTeX files can be used by many LaTeX documents, this function
331prompts upon first use for a buffer in RefTeX mode. To reset this
332link to a document, call the function with with a prefix arg.
333Calling this function several times find successive citation locations."
334 (interactive "P")
335 (when arg
336 ;; Break connection to reference buffer
337 (remprop 'reftex-bibtex-view-cite-locations :ref-buffer))
338 (let ((ref-buffer (get 'reftex-bibtex-view-cite-locations :ref-buffer)))
339 ;; Establish connection to reference buffer
340 (unless ref-buffer
341 (setq ref-buffer
342 (save-excursion
343 (completing-read
344 "Reference buffer: "
345 (delq nil
346 (mapcar
347 (lambda (b)
348 (set-buffer b)
349 (if reftex-mode (list (buffer-name b)) nil))
350 (buffer-list)))
351 nil t)))
352 (put 'reftex-bibtex-view-cite-locations :ref-buffer ref-buffer))
353 ;; Search for citations
354 (bibtex-beginning-of-entry)
355 (if (looking-at
356 "@[a-zA-Z]+[ \t\n\r]*[{(][ \t\n\r]*\\([^, \t\r\n}]+\\)")
357 (progn
358 (goto-char (match-beginning 1))
359 (reftex-view-regexp-match
360 (format reftex-find-citation-regexp-format
361 (regexp-quote (match-string 1)))
362 3 arg ref-buffer))
363 (error "Cannot find citation key in BibTeX entry"))))
364
365(defun reftex-view-regexp-match (re &optional highlight-group new ref-buffer)
366 ;; Search for RE in current document or in the document of REF-BUFFER.
367 ;; Continue the search, if the same re was searched last.
368 ;; Highlight the group HIGHLIGHT-GROUP of the match.
369 ;; When NEW is non-nil, start a new search regardless.
370 ;; Match point is displayed in another window.
371 ;; Upon success, returns the window which displays the match.
372
373 ;;; Decide if new search or continued search
374 (let* ((oldprop (get 'reftex-view-regexp-match :props))
375 (newprop (list (current-buffer) re))
376 (cont (and (not new) (equal oldprop newprop)))
377 (cnt (if cont (get 'reftex-view-regexp-match :cnt) 0))
378 (current-window (selected-window))
379 (window-conf (current-window-configuration))
380 match pop-window)
381 (switch-to-buffer-other-window (or ref-buffer (current-buffer)))
382 ;; Search
383 (condition-case nil
384 (if cont
385 (setq match (reftex-global-search-continue))
386 (reftex-access-scan-info)
387 (setq match (reftex-global-search re (reftex-all-document-files))))
388 (error nil))
389 ;; Evaluate the match.
390 (if match
391 (progn
392 (put 'reftex-view-regexp-match :props newprop)
393 (put 'reftex-view-regexp-match :cnt (incf cnt))
394 (reftex-highlight 0 (match-beginning highlight-group)
395 (match-end highlight-group))
396 (add-hook 'pre-command-hook 'reftex-highlight-shall-die)
397 (setq pop-window (selected-window)))
398 (remprop 'reftex-view-regexp-match :props)
399 (or cont (set-window-configuration window-conf)))
400 (select-window current-window)
401 (if match
402 (progn
403 (message "Match Nr. %s" cnt)
404 pop-window)
405 (if cont
406 (error "No further matches (total number of matches: %d)" cnt)
407 (error "No matches")))))
408
409(defvar reftex-global-search-marker (make-marker))
410(defun reftex-global-search (regexp file-list)
411 ;; Start a search for REGEXP in all files of FILE-LIST
412 (put 'reftex-global-search :file-list file-list)
413 (put 'reftex-global-search :regexp regexp)
414 (move-marker reftex-global-search-marker nil)
415 (reftex-global-search-continue))
416
417(defun reftex-global-search-continue ()
418 ;; Continue a global search started with `reftex-global-search'
419 (unless (get 'reftex-global-search :file-list)
420 (error "No global search to continue"))
421 (let* ((file-list (get 'reftex-global-search :file-list))
422 (regexp (get 'reftex-global-search :regexp))
423 (buf (or (marker-buffer reftex-global-search-marker)
424 (reftex-get-file-buffer-force (car file-list))))
425 (pos (or (marker-position reftex-global-search-marker) 1))
426 file)
427 ;; Take up starting position
428 (unless buf (error "No such buffer %s" buf))
429 (switch-to-buffer buf)
430 (widen)
431 (goto-char pos)
432 ;; Search and switch file if necessary
433 (if (catch 'exit
434 (while t
435 (when (re-search-forward regexp nil t)
436 (move-marker reftex-global-search-marker (point))
437 (throw 'exit t))
438 ;; No match - goto next file
439 (pop file-list)
440 (or file-list (throw 'exit nil))
441 (setq file (car file-list)
442 buf (reftex-get-file-buffer-force file))
443 (unless buf (error "Cannot access file %s" file))
444 (put 'reftex-global-search :file-list file-list)
445 (switch-to-buffer buf)
446 (widen)
447 (goto-char 1)))
448 t
449 (move-marker reftex-global-search-marker nil)
450 (error "All files processed"))))
451
452;;; reftex-vcr.el ends here