reftex: Non-Standard Environments

 
 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
            \z.
            \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
            (while
                ;; Search backward for all possible delimiters
                (re-search-backward
                 (concat "\\(^[ \t]*\n\\)\\|\\(\\\\z\\.\\)\\|"
                         "\\(\\ex[ig]?\\.\\)\\|\\(\\\\a\\.\\)")
                 nil t)
              ;; Check which delimiter was matched.
              (cond
               ((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))))))))