diff options
author | Carsten Dominik <dominik@science.uva.nl> | 1999-08-16 07:42:41 +0000 |
---|---|---|
committer | Carsten Dominik <dominik@science.uva.nl> | 1999-08-16 07:42:41 +0000 |
commit | 1a9461d069cbf51a16b453f283b1367e05c875b9 (patch) | |
tree | d05c375019904907df3a76e416841ea70ea1526b /lisp/textmodes/reftex-parse.el | |
parent | 1c25ed90c026bcd40182860500c722e632f15bd9 (diff) | |
download | emacs-1a9461d069cbf51a16b453f283b1367e05c875b9.tar.gz |
Initial revision
Diffstat (limited to 'lisp/textmodes/reftex-parse.el')
-rw-r--r-- | lisp/textmodes/reftex-parse.el | 987 |
1 files changed, 987 insertions, 0 deletions
diff --git a/lisp/textmodes/reftex-parse.el b/lisp/textmodes/reftex-parse.el new file mode 100644 index 00000000000..8b69efe9de0 --- /dev/null +++ b/lisp/textmodes/reftex-parse.el @@ -0,0 +1,987 @@ +;;; reftex-parse.el - Parser Functions for RefTeX +;;; Version: 4.5 +;;; +;;; See main file reftex.el for licensing information + +(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) + ,@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 + (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)) + (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 + "\\(\\`\\|[\n\r]\\)[ \t]*\\\\bibliography{[ \t]*\\([^}]+\\)" nil t) + (setq files + (split-string (reftex-match-string 2) + "[ \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)) + (level (cdr (assoc macro reftex-section-levels-all))) + (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)))) + (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 " ")) + 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 () + ;; Return up to 150 chars from point + ;; When point is just after a { or [, limit string to matching parenthesis + (cond + ((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 [0 0 0 0 0 0 0 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 |