3.4.6 Non-standard Environments

Some LaTeX packages define environment-like structures without using the standard ‘\begin..\end’ structure. RefTeX cannot parse these directly, but you can write your own special-purpose parser and use it instead of the name of an environment in an entry for reftex-label-alist. The function should check if point is currently in the special environment it was written to detect. If so, it must return a buffer position indicating the start of this environment. The return value must be nil on failure to detect the environment. The function is called with one argument bound. If non-nil, bound is a boundary for backwards searches which should be observed. We will discuss two examples.

Some people define abbreviations for environments, like \be for \begin{equation}, and \ee for \end{equation}. The parser function would have to search backward for these macros. When the first match is \ee, point is not in this environment. When the first match is \be, point is in this environment and the function must return the beginning of the match. To avoid scanning too far, we can also look for empty lines which cannot occur inside an equation environment. Here is the setup:

;; Setup entry in reftex-label-alist, using all defaults for equations
(setq reftex-label-alist '((detect-be-ee ?e nil nil nil nil)))

(defun detect-be-ee (bound)
  ;; Search backward for the macros or an empty line
  (if (re-search-backward
       "\\(^[ \t]*\n\\|\\\\ee\\>\\)\\|\\(\\\\be\\>\\)" bound t)
      (if (match-beginning 2)
          (match-beginning 2)  ; Return start of environment
        nil)                   ; Return nil because env is closed
    nil))                      ; Return nil for not found

A more complex example is the linguex.sty package which defines list macros ‘\ex.’, ‘\a.’, ‘\b.’ etc. for lists which are terminated by ‘\z.’ or by an empty line.

\ex.  \label{ex:12} Some text in an exotic language ...
      \a. \label{ex:13} more stuff
      \b. \label{ex:14} still more stuff
          \a. List on a deeper level
          \b. Another item
          \b. and the third one
      \b. Third item on this level.

... text after the empty line terminating all lists

The difficulty is that the ‘\a.’ lists can nest and that an empty line terminates all list levels in one go. So we have to count nesting levels between ‘\a.’ and ‘\z.’. Here is the implementation for RefTeX.

(setq reftex-label-alist
      '((detect-linguex ?x "ex:" "~\\ref{%s}" nil ("Example" "Ex."))))

(defun detect-linguex (bound)
  (let ((cnt 0))
    (catch 'exit
          ;; Search backward for all possible delimiters
           (concat "\\(^[ \t]*\n\\)\\|\\(\\\\z\\.\\)\\|"
           nil t)
        ;; Check which delimiter was matched.
         ((match-beginning 1)
          ;; empty line terminates all - return nil
          (throw 'exit nil))
         ((match-beginning 2)
          ;; \z. terminates one list level - decrease nesting count
          (decf cnt))
         ((match-beginning 3)
          ;; \ex. : return match unless there was a \z. on this level
          (throw 'exit (if (>= cnt 0) (match-beginning 3) nil)))
         ((match-beginning 4)
          ;; \a. : return match when on level 0, otherwise
          ;;       increment nesting count
          (if (>= cnt 0)
              (throw 'exit (match-beginning 4))
            (incf cnt))))))))