;;; reftex-parse.el --- parser functions for RefTeX ;; Copyright (c) 1997, 1998, 1999, 2000 Free Software Foundation, Inc. ;; Author: Carsten Dominik ;; Version: 4.17 ;; ;; This file is part of GNU Emacs. ;; GNU Emacs is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; Commentary: ;;; Code: (eval-when-compile (require 'cl)) (provide 'reftex-parse) (require 'reftex) (defmacro reftex-with-special-syntax (&rest body) `(let ((saved-syntax (syntax-table))) (unwind-protect (progn (set-syntax-table reftex-syntax-table) (let ((case-fold-search nil)) ,@body)) (set-syntax-table saved-syntax)))) (defun reftex-parse-one () "Re-parse this file." (interactive) (let ((reftex-enable-partial-scans t)) (reftex-access-scan-info '(4)))) (defun reftex-parse-all () "Re-parse entire document." (interactive) (reftex-access-scan-info '(16))) (defun reftex-do-parse (rescan &optional file) "Do a document rescan. When allowed, do only a partial scan from FILE." ;; Normalize the rescan argument (setq rescan (cond ((eq rescan t) t) ((eq rescan 1) 1) ((equal rescan '(4)) t) ((equal rescan '(16)) 1) (t 1))) ;; Partial scans only when allowed (unless reftex-enable-partial-scans (setq rescan 1)) ;; Do the scanning. (let* ((old-list (symbol-value reftex-docstruct-symbol)) (master (reftex-TeX-master-file)) (true-master (file-truename master)) (master-dir (file-name-as-directory (file-name-directory master))) (file (or file (buffer-file-name))) (true-file (file-truename file)) (bibview-cache (assq 'bibview-cache old-list)) (index-tags (cdr (assq 'index-tags old-list))) from-file appendix docstruct tmp) ;; Make sure replacement is really an option here (when (and (eq rescan t) (not (and (member (list 'bof file) old-list) (member (list 'eof file) old-list)))) ;; Scan whole document because no such file section exists (setq rescan 1)) (when (string= true-file true-master) ;; Scan whole document because this file is the master (setq rescan 1)) ;; From which file do we start? (setq from-file (cond ((eq rescan t) (or file master)) ((eq rescan 1) master) (t (error "This should not happen (reftex-do-parse)")))) ;; Reset index-tags if we scan everything (if (equal rescan 1) (setq index-tags nil)) ;; Find active toc entry and initialize section-numbers (setq reftex-active-toc (reftex-last-assoc-before-elt 'toc (list 'bof from-file) old-list) appendix (reftex-last-assoc-before-elt 'appendix (list 'bof from-file) old-list)) (reftex-init-section-numbers reftex-active-toc appendix) (if (eq rescan 1) (message "Scanning entire document...") (message "Scanning document from %s..." from-file)) (reftex-with-special-syntax (save-window-excursion (save-excursion (unwind-protect (setq docstruct (reftex-parse-from-file from-file docstruct master-dir)) (reftex-kill-temporary-buffers))))) (message "Scanning document... done") ;; Turn the list around. (setq docstruct (nreverse docstruct)) ;; Set or insert (setq docstruct (reftex-replace-label-list-segment old-list docstruct (eq rescan 1))) ;; Add all missing information (unless (assq 'label-numbers docstruct) (push (cons 'label-numbers nil) docstruct)) (unless (assq 'master-dir docstruct) (push (cons 'master-dir master-dir) docstruct)) (unless (assq 'bibview-cache docstruct) (push (cons 'bibview-cache (cdr bibview-cache)) docstruct)) (let* ((bof1 (memq (assq 'bof docstruct) docstruct)) (bof2 (assq 'bof (cdr bof1))) (is-multi (not (not (and bof1 bof2)))) (entry (or (assq 'is-multi docstruct) (car (push (list 'is-multi is-multi) docstruct))))) (setcdr entry (cons is-multi nil))) (and index-tags (setq index-tags (sort index-tags 'string<))) (let ((index-tag-cell (assq 'index-tags docstruct))) (if index-tag-cell (setcdr index-tag-cell index-tags) (push (cons 'index-tags index-tags) docstruct))) (unless (assq 'xr docstruct) (let* ((allxr (reftex-all-assq 'xr-doc docstruct)) (alist (mapcar (lambda (x) (if (setq tmp (reftex-locate-file (nth 2 x) "tex" master-dir)) (cons (nth 1 x) tmp) (message "Can't find external document %s" (nth 2 x)) nil)) allxr)) (alist (delq nil alist)) (allprefix (delq nil (mapcar 'car alist))) (regexp (if allprefix (concat "\\`\\(" (mapconcat 'identity allprefix "\\|") "\\)") "\\\\\\\\\\\\"))) ; this will never match (push (list 'xr alist regexp) docstruct))) (set reftex-docstruct-symbol docstruct) (put reftex-docstruct-symbol 'modified t))) (defun reftex-everything-regexp () (if reftex-support-index reftex-everything-regexp reftex-everything-regexp-no-index)) (defun reftex-all-document-files (&optional relative) "Return a list of all files belonging to the current document. When RELATIVE is non-nil, give file names relative to directory of master file." (let* ((all (symbol-value reftex-docstruct-symbol)) (master-dir (file-name-directory (reftex-TeX-master-file))) (re (concat "\\`" (regexp-quote master-dir))) file-list tmp file) (while (setq tmp (assoc 'bof all)) (setq file (nth 1 tmp) all (cdr (memq tmp all))) (and relative (string-match re file) (setq file (substring file (match-end 0)))) (push file file-list)) (nreverse file-list))) (defun reftex-parse-from-file (file docstruct master-dir) ;; Scan the buffer for labels and save them in a list. (let ((regexp (reftex-everything-regexp)) (bound 0) file-found tmp include-file (level 1) (highest-level 100) toc-entry index-entry next-buf buf) (catch 'exit (debug) (setq file-found (reftex-locate-file file "tex" master-dir)) (if (and (not file-found) (setq buf (reftex-get-buffer-visiting file))) (setq file-found (buffer-file-name buf))) (unless file-found (push (list 'file-error file) docstruct) (throw 'exit nil)) (save-excursion (message "Scanning file %s" file) (set-buffer (setq next-buf (reftex-get-file-buffer-force file-found (not (eq t reftex-keep-temporary-buffers))))) ;; Begin of file mark (setq file (buffer-file-name)) (push (list 'bof file) docstruct) (reftex-with-special-syntax (save-excursion (save-restriction (widen) (goto-char 1) (while (re-search-forward regexp nil t) (cond ((match-end 1) ;; It is a label (push (reftex-label-info (reftex-match-string 1) file bound) docstruct)) ((match-end 3) ;; It is a section (setq bound (point)) ;; Insert in List (setq toc-entry (reftex-section-info file)) (when toc-entry ;; It can happen that section info returns nil (setq level (nth 5 toc-entry)) (setq highest-level (min highest-level level)) (if (= level highest-level) (message "Scanning %s %s ..." (car (rassoc level reftex-section-levels-all)) (nth 6 toc-entry))) (push toc-entry docstruct) (setq reftex-active-toc toc-entry))) ((match-end 7) ;; It's an include or input (setq include-file (reftex-match-string 7)) ;; Test if this file should be ignored (unless (delq nil (mapcar (lambda (x) (string-match x include-file)) reftex-no-include-regexps)) ;; Parse it (setq docstruct (reftex-parse-from-file include-file docstruct master-dir)))) ((match-end 9) ;; Appendix starts here (reftex-init-section-numbers nil t) (push (cons 'appendix t) docstruct)) ((match-end 10) ;; Index entry (when reftex-support-index (setq index-entry (reftex-index-info file)) (when index-entry (add-to-list 'index-tags (nth 1 index-entry)) (push index-entry docstruct)))) ((match-end 11) ;; A macro with label (save-excursion (let* ((mac (reftex-match-string 11)) (label (progn (goto-char (match-end 11)) (save-match-data (reftex-no-props (reftex-nth-arg-wrapper mac))))) (typekey (nth 1 (assoc mac reftex-env-or-mac-alist))) (entry (progn (if typekey ;; A typing macro (goto-char (match-end 0)) ;; A neutral macro (goto-char (match-end 11)) (reftex-move-over-touching-args)) (reftex-label-info label file bound nil nil)))) (push entry docstruct)))) (t (error "This should not happen (reftex-parse-from-file)"))) ) ;; Find bibliography statement (when (setq tmp (reftex-locate-bibliography-files master-dir)) (push (cons 'bib tmp) docstruct)) (goto-char 1) (when (re-search-forward "\\(\\`\\|[\n\r]\\)[ \t]*\\\\begin{thebibliography}" nil t) (push (cons 'thebib file) docstruct)) ;; Find external document specifications (goto-char 1) (while (re-search-forward "[\n\r][ \t]*\\\\externaldocument\\(\\[\\([^]]*\\)\\]\\)?{\\([^}]+\\)}" nil t) (push (list 'xr-doc (reftex-match-string 2) (reftex-match-string 3)) docstruct)) ;; End of file mark (push (list 'eof file) docstruct))))) ;; Kill the scanned buffer (reftex-kill-temporary-buffers next-buf)) ;; Return the list docstruct)) (defun reftex-locate-bibliography-files (master-dir &optional files) ;; Scan buffer for bibliography macro and return file list. (unless files (save-excursion (goto-char (point-min)) (if (re-search-forward (concat "\\(\\`\\|[\n\r]\\)[^%]*\\\\\\(" (mapconcat 'identity reftex-bibliography-commands "\\|") "\\){[ \t]*\\([^}]+\\)") nil t) (setq files (split-string (reftex-match-string 3) "[ \t\n\r]*,[ \t\n\r]*"))))) (when files (setq files (mapcar (lambda (x) (if (or (member x reftex-bibfile-ignore-list) (delq nil (mapcar (lambda (re) (string-match re x)) reftex-bibfile-ignore-regexps))) ;; excluded file nil ;; find the file (reftex-locate-file x "bib" master-dir))) files)) (delq nil files))) (defun reftex-replace-label-list-segment (old insert &optional entirely) ;; Replace the segment in OLD which corresponds to INSERT. ;; Works with side effects, directly changes old. ;; If entirely is t, just return INSERT. ;; This function also makes sure the old toc markers do not point anywhere. (cond (entirely (reftex-silence-toc-markers old (length old)) insert) (t (let* ((new old) (file (nth 1 (car insert))) (eof-list (member (list 'eof file) old)) (bof-list (member (list 'bof file) old)) n) (if (not (and bof-list eof-list)) (error "Cannot splice") ;; Splice (reftex-silence-toc-markers bof-list (- (length bof-list) (length eof-list))) (setq n (- (length old) (length bof-list))) (setcdr (nthcdr n new) (cdr insert)) (setcdr (nthcdr (1- (length new)) new) (cdr eof-list))) new)))) (defun reftex-section-info (file) ;; Return a section entry for the current match. ;; Carefull: This function expects the match-data to be still in place! (let* ((marker (set-marker (make-marker) (1- (match-beginning 3)))) (macro (reftex-match-string 3)) (prefix (save-match-data (if (string-match "begin{\\([^}]+\\)}" macro) (match-string 1 macro)))) (level-exp (cdr (assoc macro reftex-section-levels-all))) (level (if (symbolp level-exp) (save-match-data (funcall level-exp)) level-exp)) (star (= ?* (char-after (match-end 3)))) (unnumbered (or star (< level 0))) (level (abs level)) (section-number (reftex-section-number level unnumbered)) (text1 (save-match-data (save-excursion (reftex-context-substring prefix)))) (literal (buffer-substring-no-properties (1- (match-beginning 3)) (min (point-max) (+ (match-end 0) (length text1) 1)))) ;; Literal can be too short since text1 too short. No big problem. (text (reftex-nicify-text text1))) ;; Add section number and indentation (setq text (concat (make-string (* reftex-level-indent level) ?\ ) (if (nth 1 reftex-label-menu-flags) ; section number flag (concat section-number " ")) (if prefix (concat (capitalize prefix) ": ") "") text)) (list 'toc "toc" text file marker level section-number literal (marker-position marker)))) (defun reftex-ensure-index-support (&optional abort) ;; When index support is turned off, ask to turn it on and ;; set the current prefix argument so that `reftex-access-scan-info' ;; will rescan the entire document. (cond (reftex-support-index t) ((y-or-n-p "Turn on index support and rescan entire document? ") (setq reftex-support-index 'demanded current-prefix-arg '(16))) (t (if abort (error "No index support") (message "No index support") (ding) (sit-for 1))))) (defun reftex-index-info-safe (file) (reftex-with-special-syntax (reftex-index-info file))) (defvar test-dummy) (defun reftex-index-info (file) ;; Return an index entry for the current match. ;; Carefull: This function expects the match-data to be still in place! (catch 'exit (let* ((macro (reftex-match-string 10)) (bom (match-beginning 10)) (boa (match-end 10)) (entry (or (assoc macro reftex-index-macro-alist) (throw 'exit nil))) (exclude (nth 3 entry)) ;; The following is a test if this match should be excluded (test-dummy (and (fboundp exclude) (funcall exclude) (throw 'exit nil))) (itag (nth 1 entry)) (prefix (nth 2 entry)) (index-tag (cond ((stringp itag) itag) ((integerp itag) (progn (goto-char boa) (or (reftex-nth-arg itag (nth 6 entry)) "idx"))) (t "idx"))) (arg (or (progn (goto-char boa) (reftex-nth-arg (nth 5 entry) (nth 6 entry))) "")) (end-of-args (progn (goto-char boa) (reftex-move-over-touching-args) (point))) (end-of-context (progn (skip-chars-forward "^ \t\n\r") (point))) (begin-of-context (progn (goto-char bom) (skip-chars-backward "^ \t\r\n") (point))) (context (buffer-substring-no-properties begin-of-context end-of-context)) (key-end (if (string-match reftex-index-key-end-re arg) (1+ (match-beginning 0)))) (rawkey (substring arg 0 key-end)) (key (if prefix (concat prefix rawkey) rawkey)) (sortkey (downcase key)) (showkey (mapconcat 'identity (split-string key reftex-index-level-re) " ! "))) (goto-char end-of-args) ;; 0 1 2 3 4 5 6 7 8 9 (list 'index index-tag context file bom arg key showkey sortkey key-end)))) (defun reftex-short-context (env parse &optional bound derive) ;; Get about one line of useful context for the label definition at point. (if (consp parse) (setq parse (if derive (cdr parse) (car parse)))) (reftex-nicify-text (cond ((null parse) (save-excursion (reftex-context-substring))) ((eq parse t) (if (string= env "section") ;; special treatment for section labels (save-excursion (if (and (re-search-backward reftex-section-or-include-regexp (point-min) t) (match-end 2)) (progn (goto-char (match-end 0)) (reftex-context-substring)) (if reftex-active-toc (progn (string-match "{\\([^}]*\\)" (nth 7 reftex-active-toc)) (match-string 1 (nth 7 reftex-active-toc))) "SECTION HEADING NOT FOUND"))) (save-excursion (goto-char reftex-default-context-position) (unless (eq (string-to-char env) ?\\) (reftex-move-over-touching-args)) (reftex-context-substring)))) ((stringp parse) (save-excursion (if (re-search-backward parse bound t) (progn (goto-char (match-end 0)) (reftex-context-substring)) "NO MATCH FOR CONTEXT REGEXP"))) ((integerp parse) (or (save-excursion (goto-char reftex-default-context-position) (reftex-nth-arg parse (nth 6 (assoc env reftex-env-or-mac-alist)))) "")) ((fboundp parse) ;; A hook function. Call it. (save-excursion (condition-case error-var (funcall parse env) (error (format "HOOK ERROR: %s" (cdr error-var)))))) (t "ILLEGAL VALUE OF PARSE")))) (defun reftex-where-am-I () ;; Return the docstruct entry above point. Actually returns a cons ;; cell in which the cdr is a flag indicating if the information is ;; exact (t) or approximate (nil). (let ((docstruct (symbol-value reftex-docstruct-symbol)) (cnt 0) rtn found) (save-excursion (while (not rtn) (incf cnt) (setq found (re-search-backward (reftex-everything-regexp) nil t)) (setq rtn (cond ((not found) ;; no match (or (car (member (list 'bof (buffer-file-name)) docstruct)) (not (setq cnt 2)) (assq 'bof docstruct) ;; for safety reasons 'corrupted)) ((match-end 1) ;; Label (assoc (reftex-match-string 1) (symbol-value reftex-docstruct-symbol))) ((match-end 3) ;; Section (goto-char (1- (match-beginning 3))) (let* ((list (member (list 'bof (buffer-file-name)) docstruct)) (endelt (car (member (list 'eof (buffer-file-name)) list))) rtn1) (while (and list (not (eq endelt (car list)))) (if (and (eq (car (car list)) 'toc) (string= (buffer-file-name) (nth 3 (car list)))) (cond ((equal (point) (or (and (markerp (nth 4 (car list))) (marker-position (nth 4 (car list)))) (nth 8 (car list)))) ;; Fits with marker position or recorded position (setq rtn1 (car list) list nil)) ((looking-at (reftex-make-regexp-allow-for-ctrl-m (nth 7 (car list)))) ;; Same title (setq rtn1 (car list) list nil cnt 2)))) (pop list)) rtn1)) ((match-end 7) ;; Input or include... (car (member (list 'eof (reftex-locate-file (reftex-match-string 7) "tex" (cdr (assq 'master-dir docstruct)))) docstruct))) ((match-end 9) (assq 'appendix (symbol-value reftex-docstruct-symbol))) ((match-end 10) ;; Index entry (when reftex-support-index (let* ((index-info (save-excursion (reftex-index-info-safe nil))) (list (member (list 'bof (buffer-file-name)) docstruct)) (endelt (car (member (list 'eof (buffer-file-name)) list))) dist last-dist last (n 0)) ;; Check all index entries with equal text (while (and list (not (eq endelt (car list)))) (when (and (eq (car (car list)) 'index) (string= (nth 2 index-info) (nth 2 (car list)))) (incf n) (setq dist (abs (- (point) (nth 4 (car list))))) (if (or (not last-dist) (< dist last-dist)) (setq last-dist dist last (car list)))) (setq list (cdr list))) ;; We are sure if we have only one, or a zero distance (cond ((or (= n 1) (= dist 0)) last) ((> n 1) (setq cnt 2) last) (t nil))))) ((match-end 11) (save-excursion (goto-char (match-end 11)) (assoc (reftex-no-props (reftex-nth-arg-wrapper (reftex-match-string 11))) (symbol-value reftex-docstruct-symbol)))) (t (error "This should not happen (reftex-where-am-I)")))))) (cons rtn (eq cnt 1)))) (defun reftex-notice-new (&optional n force) "Hook to handshake with RefTeX after something new has been inserted." ;; Add a new entry to the docstruct list. If it is a section, renumber ;; the following sections. ;; FIXME: Put in a WHAT parameter ;; When N is given, go back that many matches of reftex-everything-regexp ;; When FORCE is non-nil, also insert if `reftex-where-am-I' was uncertain. (condition-case nil (catch 'exit (unless reftex-mode (throw 'exit nil)) (reftex-access-scan-info) (let* ((docstruct (symbol-value reftex-docstruct-symbol)) here-I-am appendix tail entry star level section-number context) (save-excursion (when (re-search-backward (reftex-everything-regexp) nil t (or n 1)) ;; Find where we are (setq here-I-am (reftex-where-am-I)) (or here-I-am (throw 'exit nil)) (unless (or force (cdr here-I-am)) (throw 'exit nil)) (setq tail (memq (car here-I-am) docstruct)) (or tail (throw 'exit nil)) (setq reftex-active-toc (reftex-last-assoc-before-elt 'toc (car here-I-am) docstruct) appendix (reftex-last-assoc-before-elt 'appendix (car here-I-am) docstruct)) ;; Initialize section numbers (if (eq (car (car here-I-am)) 'appendix) (reftex-init-section-numbers nil t) (reftex-init-section-numbers reftex-active-toc appendix)) ;; Match the section command (when (re-search-forward (reftex-everything-regexp) nil t) (cond ((match-end 1) (push (reftex-label-info (reftex-match-string 1) buffer-file-name) (cdr tail))) ((match-end 3) (setq star (= ?* (char-after (match-end 3))) entry (reftex-section-info (buffer-file-name)) level (nth 5 entry)) ;; Insert the section info (push entry (cdr tail)) ;; We are done unless we use section numbers (unless (nth 1 reftex-label-menu-flags) (throw 'exit nil)) ;; Update the remaining toc items (setq tail (cdr tail)) (while (and (setq tail (memq (assq 'toc (cdr tail)) tail)) (setq entry (car tail)) (>= (nth 5 entry) level)) (setq star (string-match "\\*" (nth 6 entry)) context (nth 2 entry) section-number (reftex-section-number (nth 5 entry) star)) (when (string-match "\\`\\([ \t]*\\)\\([.0-9A-Z]+\\)\\(.*\\)" context) (when (and (not appendix) (>= (string-to-char (match-string 2)) ?A)) ;; Just entered the appendex. Get out. (throw 'exit nil)) ;; Change the section number. (setf (nth 2 entry) (concat (match-string 1 context) section-number (match-string 3 context)))))) ((match-end 10) ;; Index entry (and reftex-support-index (setq entry (reftex-index-info-safe buffer-file-name)) ;; FIXME: (add-to-list 'index-tags (nth 1 index-entry)) (push entry (cdr tail)))))))))) (error nil)) ) (defsubst reftex-move-to-previous-arg (&optional bound) ;; Assuming that we are in front of a macro argument, ;; move backward to the closing parenthesis of the previous argument. ;; This function understands the splitting of macros over several lines ;; in TeX. (cond ;; Just to be quick: ((memq (preceding-char) '(?\] ?\}))) ;; Do a search ((and reftex-allow-detached-macro-args (re-search-backward "[]}][ \t]*[\n\r]?\\([ \t]*%[^\n\r]*[\n\r]\\)*[ \t]*\\=" bound t)) (goto-char (1+ (match-beginning 0))) t) (t nil))) (defun reftex-what-macro-safe (which &optional bound) ;; reftex-what-macro with special syntax table. (reftex-with-special-syntax (reftex-what-macro which bound))) (defun reftex-what-macro (which &optional bound) ;; Find out if point is within the arguments of any TeX-macro. ;; The return value is either ("\\macro" . (point)) or a list of them. ;; If WHICH is nil, immediately return nil. ;; If WHICH is 1, return innermost enclosing macro. ;; If WHICH is t, return list of all macros enclosing point. ;; If WHICH is a list of macros, look only for those macros and return the ;; name of the first macro in this list found to enclose point. ;; If the optional BOUND is an integer, bound backwards directed ;; searches to this point. If it is nil, limit to nearest \section - ;; like statement. ;; This function is pretty stable, but can be fooled if the text contains ;; things like \macro{aa}{bb} where \macro is defined to take only one ;; argument. As RefTeX cannot know this, the string "bb" would still be ;; considered an argument of macro \macro. (unless reftex-section-regexp (reftex-compile-variables)) (catch 'exit (if (null which) (throw 'exit nil)) (let ((bound (or bound (save-excursion (re-search-backward reftex-section-regexp nil 1) (point)))) pos cmd-list cmd cnt cnt-opt entry) (save-restriction (save-excursion (narrow-to-region (max 1 bound) (point-max)) ;; move back out of the current parenthesis (while (condition-case nil (progn (up-list -1) t) (error nil)) (setq cnt 1 cnt-opt 0) ;; move back over any touching sexps (while (and (reftex-move-to-previous-arg bound) (condition-case nil (progn (backward-sexp) t) (error nil))) (if (eq (following-char) ?\[) (incf cnt-opt)) (incf cnt)) (setq pos (point)) (when (and (or (= (following-char) ?\[) (= (following-char) ?\{)) (re-search-backward "\\\\[*a-zA-Z]+\\=" nil t)) (setq cmd (reftex-match-string 0)) (when (looking-at "\\\\begin{[^}]*}") (setq cmd (reftex-match-string 0) cnt (1- cnt))) ;; This does ignore optional arguments. Very hard to fix. (when (setq entry (assoc cmd reftex-env-or-mac-alist)) (if (> cnt (or (nth 4 entry) 100)) (setq cmd nil))) (cond ((null cmd)) ((eq t which) (push (cons cmd (point)) cmd-list)) ((or (eq 1 which) (member cmd which)) (throw 'exit (cons cmd (point)))))) (goto-char pos))) (nreverse cmd-list))))) (defun reftex-what-environment (which &optional bound) ;; Find out if point is inside a LaTeX environment. ;; The return value is (e.g.) either ("equation" . (point)) or a list of ;; them. ;; If WHICH is nil, immediately return nil. ;; If WHICH is 1, return innermost enclosing environment. ;; If WHICH is t, return list of all environments enclosing point. ;; If WHICH is a list of environments, look only for those environments and ;; return the name of the first environment in this list found to enclose ;; point. ;; If the optional BOUND is an integer, bound backwards directed searches to ;; this point. If it is nil, limit to nearest \section - like statement. (unless reftex-section-regexp (reftex-compile-variables)) (catch 'exit (save-excursion (if (null which) (throw 'exit nil)) (let ((bound (or bound (save-excursion (re-search-backward reftex-section-regexp nil 1) (point)))) env-list end-list env) (while (re-search-backward "\\\\\\(begin\\|end\\){\\([^}]+\\)}" bound t) (setq env (buffer-substring-no-properties (match-beginning 2) (match-end 2))) (cond ((string= (match-string 1) "end") (push env end-list)) ((equal env (car end-list)) (setq end-list (cdr end-list))) ((eq t which) (push (cons env (point)) env-list)) ((or (eq 1 which) (member env which)) (throw 'exit (cons env (point)))))) (nreverse env-list))))) (defun reftex-what-special-env (which &optional bound) ;; Run the special environment parsers and return the matches. ;; ;; The return value is (e.g.) either ("my-parser-function" . (point)) ;; or a list of them. ;; If WHICH is nil, immediately return nil. ;; If WHICH is 1, return innermost enclosing environment. ;; If WHICH is t, return list of all environments enclosing point. ;; If WHICH is a list of environments, look only for those environments and ;; return the name of the first environment in this list found to enclose ;; point. (unless reftex-section-regexp (reftex-compile-variables)) (catch 'exit (save-excursion (if (null reftex-special-env-parsers) (throw 'exit nil)) (if (null which) (throw 'exit nil)) (let ((bound (or bound (save-excursion (re-search-backward reftex-section-regexp nil 1) (point)))) (fun-list (if (listp which) (mapcar (lambda (x) (if (memq x which) x nil)) reftex-special-env-parsers) reftex-special-env-parsers)) specials rtn) ;; Call all functions (setq specials (mapcar (lambda (fun) (save-excursion (setq rtn (and fun (funcall fun bound))) (if rtn (cons (symbol-name fun) rtn) nil))) fun-list)) ;; Delete the non-matches (setq specials (delq nil specials)) ;; Sort (setq specials (sort specials (lambda (a b) (> (cdr a) (cdr b))))) (if (eq which t) specials (car specials)))))) (defsubst reftex-move-to-next-arg (&optional ignore) ;; Assuming that we are at the end of a macro name or a macro argument, ;; move forward to the opening parenthesis of the next argument. ;; This function understands the splitting of macros over several lines ;; in TeX. (cond ;; Just to be quick: ((memq (following-char) '(?\[ ?\{))) ;; Do a search ((and reftex-allow-detached-macro-args (looking-at "[ \t]*[\n\r]?\\([ \t]*%[^\n\r]*[\n\r]\\)*[ \t]*[[{]")) (goto-char (1- (match-end 0))) t) (t nil))) (defun reftex-nth-arg-wrapper (key) (let ((entry (assoc key reftex-env-or-mac-alist))) (reftex-nth-arg (nth 5 entry) (nth 6 entry)))) (defun reftex-nth-arg (n &optional opt-args) ;; Return the nth following {} or [] parentheses content. ;; OPT-ARGS is a list of argument numbers which are optional. ;; If we are sitting at a macro start, skip to end of macro name. (and (eq (following-char) ?\\) (skip-chars-forward "a-zA-Z*\\\\")) (if (= n 1000) ;; Special case: Skip all touching arguments (progn (reftex-move-over-touching-args) (reftex-context-substring)) ;; Do the real thing. (let ((cnt 1)) (when (reftex-move-to-next-arg) (while (< cnt n) (while (and (member cnt opt-args) (eq (following-char) ?\{)) (incf cnt)) (when (< cnt n) (unless (and (condition-case nil (or (forward-list 1) t) (error nil)) (reftex-move-to-next-arg) (incf cnt)) (setq cnt 1000)))) (while (and (memq cnt opt-args) (eq (following-char) ?\{)) (incf cnt))) (if (and (= n cnt) (> (skip-chars-forward "{\\[") 0)) (reftex-context-substring) nil)))) (defun reftex-move-over-touching-args () (condition-case nil (while (memq (following-char) '(?\[ ?\{)) (forward-list 1)) (error nil))) (defun reftex-context-substring (&optional to-end) ;; Return up to 150 chars from point ;; When point is just after a { or [, limit string to matching parenthesis (cond (to-end ;; Environment - find next \end (buffer-substring-no-properties (point) (min (+ (point) 150) (save-match-data ;; FIXME: THis is not perfect (if (re-search-forward "\\\\end{" nil t) (match-beginning 0) (point-max)))))) ((or (= (preceding-char) ?\{) (= (preceding-char) ?\[)) ;; Inside a list - get only the list. (buffer-substring-no-properties (point) (min (+ (point) 150) (point-max) (condition-case nil (progn (up-list 1) (1- (point))) (error (point-max)))))) (t ;; no list - just grab 150 characters (buffer-substring-no-properties (point) (min (+ (point) 150) (point-max)))))) ;; Variable holding the vector with section numbers (defvar reftex-section-numbers (make-vector reftex-max-section-depth 0)) (defun reftex-init-section-numbers (&optional toc-entry appendix) ;; Initialize the section numbers with zeros or with what is found ;; in the toc entry. (let* ((level (or (nth 5 toc-entry) -1)) (numbers (nreverse (split-string (or (nth 6 toc-entry) "") "\\."))) (depth (1- (length reftex-section-numbers))) (i depth) number-string) (while (>= i 0) (if (> i level) (aset reftex-section-numbers i 0) (setq number-string (or (car numbers) "0")) (if (string-match "\\`[A-Z]\\'" number-string) (aset reftex-section-numbers i (- (string-to-char number-string) ?A -1)) (aset reftex-section-numbers i (string-to-int number-string))) (pop numbers)) (decf i))) (put 'reftex-section-numbers 'appendix appendix)) (defun reftex-section-number (&optional level star) ;; Return a string with the current section number. ;; When LEVEL is non-nil, increase section numbers on that level. (let* ((depth (1- (length reftex-section-numbers))) idx n (string "") (appendix (get 'reftex-section-numbers 'appendix))) (when level (when (and (> level -1) (not star)) (aset reftex-section-numbers level (1+ (aref reftex-section-numbers level)))) (setq idx (1+ level)) (when (not star) (while (<= idx depth) (aset reftex-section-numbers idx 0) (incf idx)))) (setq idx 0) (while (<= idx depth) (setq n (aref reftex-section-numbers idx)) (setq string (concat string (if (not (string= string "")) "." "") (int-to-string n))) (incf idx)) (save-match-data (if (string-match "\\`\\([@0]\\.\\)+" string) (setq string (replace-match "" nil nil string))) (if (string-match "\\(\\.0\\)+\\'" string) (setq string (replace-match "" nil nil string))) (if (and appendix (string-match "\\`[0-9]+" string)) (setq string (concat (char-to-string (1- (+ ?A (string-to-int (match-string 0 string))))) (substring string (match-end 0)))))) (if star (concat (make-string (1- (length string)) ?\ ) "*") string))) ;;; reftex-parse.el ends here