branch: externals/tex-parens
commit 6002c20edc1eabaa412a603b4e52701925b27768
Author: Paul Nelson <[email protected]>
Commit: Paul Nelson <[email protected]>

    make the construction of paren-like pairs systematic
---
 tex-parens.el | 398 +++++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 267 insertions(+), 131 deletions(-)

diff --git a/tex-parens.el b/tex-parens.el
index 5d0d87309c..69a0718036 100644
--- a/tex-parens.el
+++ b/tex-parens.el
@@ -49,6 +49,123 @@
 
 ;;; Code:
 
+;; (defvar tex-parens-pairs
+;;   '(("(" . ")")
+;;     ("\\Big(" . "\\Big)")
+;;     ("\\left(" . "\\right)")
+;;     ("[" . "]")
+;;     ("\\left[" . "\\right]")
+;;     ("{" . "}")
+;;     ("\\{" . "\\}")
+;;     ("\\left\\{" . "\\right\\}")
+;;     ("\\langle" . "\\rangle")
+;;     ("\\left\\langle" . "\\right\\rangle")
+;;     ("\\lvert" . "\\rvert")
+;;     ("\\left\\lvert" . "\\right\\rvert")
+;;     ("\\lVert" . "\\rVert")
+;;     ("\\left\\lVert" . "\\right\\rVert")
+;;     ("\\left|" . "\\right|")
+;;     ("\\left." . "\\right.")
+;;     ("$" . "$")
+;;     ("$$" . "$$")
+;;     ("\\(" . "\\)")
+;;     ("\\[" . "\\]")
+;;     ("``" . "''")))
+
+(defun tex-parens--beginning-of-defun ()
+  (interactive)
+  (re-search-backward "^\\\\begin{[^}]+}" nil t))
+
+(defun tex-parens--end-of-defun ()
+  (interactive)
+  (re-search-forward "^\\\\end{[^}]+}\n" nil t))
+
+(defun tex-parens--generate-pairs ()
+  (let ((unambiguous-parens
+         '(("(" . ")")
+           ("[" . "]")
+           ("\\{" . "\\}")
+           ("\\langle" . "\\rangle")
+           ("\\lvert" . "\\rvert")
+           ("\\lVert" . "\\rVert")
+           ("\\lfloor" . "\\rfloor")
+           ("\\lceil" . "\\rceil")))
+        (ambiguous-parens
+         '("|" "\\|" "\\vert" "\\Vert"))
+        (unambiguous-modifiers
+         '(("\\left" . "\\right")
+           ("\\bigl" . "\\bigr")
+           ("\\Bigl" . "\\Bigr")
+           ("\\biggl" . "\\biggr")
+           ("\\Biggl" . "\\Biggr")))
+        (ambiguous-modifiers
+         '("\\big"
+           "\\Big"
+           "\\bigg"
+           "\\Bigg"))
+        (other-parens
+         '(("``" . "''")
+           ("$" . "$")
+           ("{" . "}")
+           ("$$" . "$$")
+           ("\\(" . "\\)")
+           ("\\[" . "\\]")
+           ("\\left." . "\\right."))))
+    (append
+     other-parens
+     unambiguous-parens
+     (cl-reduce #'append
+                (mapcar
+                 (lambda (unam-par)
+                   (mapcar
+                    (lambda (unam-mod)
+                      (cons (concat (car unam-mod) (car unam-par))
+                            (concat (cdr unam-mod) (cdr unam-par))))
+                    unambiguous-modifiers))
+                 unambiguous-parens))
+     (cl-reduce #'append
+                (mapcar
+                 (lambda (unam-par)
+                   (mapcar
+                    (lambda (am-mod)
+                      (cons (concat am-mod (car unam-par))
+                            (concat am-mod (cdr unam-par))))
+                    ambiguous-modifiers))
+                 unambiguous-parens))
+     (cl-reduce #'append
+                (mapcar
+                 (lambda (am-par)
+                   (mapcar
+                    (lambda (unam-mod)
+                      (cons (concat (car unam-mod) am-par)
+                            (concat (cdr unam-mod) am-par)))
+                    unambiguous-modifiers))
+                 ambiguous-parens)))))
+
+
+;; (defun tex-parens-open ()
+;;   (mapcar #'car tex-parens-pairs))
+
+;; (defun tex-parens-close ()
+;;   (mapcar #'cdr tex-parens-pairs))
+
+;; (defun tex-parens-delims ()
+;;   (append (tex-parens-open) (tex-parens-close)))
+
+;; (defun tex-parens-regexp ()
+;;   (concat (regexp-opt (tex-parens-delims))
+;;           "\\|\\\\begin{[^}]+}\\|\\\\end{[^}]+}"))
+
+;; (defun tex-parens-reverse-regexp ()
+;;   (concat "}[^{]+{nigeb\\\\\\|}[^{]+{dne\\\\\\|"
+;;           (regexp-opt (mapcar #'reverse (tex-parens-delims)))))
+
+(defvar tex-parens--pairs nil)
+(defvar tex-parens--pairs-swap nil)
+(defvar tex-parens--delims nil)
+(defvar tex-parens--regexp nil)
+(defvar tex-parens--regexp-reverse nil)
+
 (defun tex-parens-setup ()
   "Hook function for LaTeX-mode that sets up tex-parens."
   (setq
@@ -57,107 +174,22 @@
                                (key-binding [right])
                                #'backward-char #'forward-char 
#'tex-parens-down-list)))
   (setq-local beginning-of-defun-function #'tex-parens--beginning-of-defun)
-  (setq-local end-of-defun-function #'tex-parens--end-of-defun)
+  (setq end-of-defun-function #'tex-parens--end-of-defun)
+  (setq tex-parens--pairs (tex-parens--generate-pairs))
+  (setq tex-parens--pairs-swap
+        (mapcar (lambda (x) (cons (cdr x) (car x))) tex-parens--pairs))
+  (setq tex-parens--delims (append (mapcar #'car tex-parens--pairs)
+                                   (mapcar #'cdr tex-parens--pairs)))
+  (setq tex-parens--regexp
+        (concat (regexp-opt tex-parens--delims)
+                "\\|\\\\begin{[^}]+}\\|\\\\end{[^}]+}"))
+  (setq tex-parens--regexp-reverse
+        (concat "}[^{]+{nigeb\\\\\\|}[^{]+{dne\\\\\\|"
+                (regexp-opt (mapcar #'reverse tex-parens--delims))))
   ;; don't know why, but this freezes Emacs:
   ;; (setq-local forward-sexp-function #'tex-parens-forward-sexp)
   )
 
-(defun tex-parens--beginning-of-defun ()
-  (interactive)
-  (re-search-backward "^\\\\begin{[^}]+}" nil t))
-
-(defun tex-parens--end-of-defun ()
-  (interactive)
-  (re-search-forward "^\\\\end{[^}]+}\n" nil t))
-
-(defun tex-parens-forward-sexp (&optional arg)
-  "Function to use as `forward-sexp-function' in LaTeX-mode."
-  (interactive "^p")
-  (or arg (setq arg 1))
-  (while (> arg 0)
-    (tex-parens-forward-sexp-1)
-    (setq arg (1- arg)))
-  (while (< arg 0)
-    (tex-parens-backward-sexp)
-    (setq arg (1+ arg))))
-
-(defun tex-parens-forward-sexp-1 ()
-  "Internal forward-sexp function.
-This function is a wrapper around `forward-sexp' that uses
-tex-parens to identify the next delimiter.  If `forward-sexp'
-does not take us past the starting point of the next delimiter, then
-do that.  Otherwise, do `tex-parens-forward-list'."
-  (interactive)
-  (let ((delim-beg (save-excursion
-                     (tex-parens--forward-delim)
-                     (match-beginning 0)))
-        (vanilla (save-excursion
-                   (goto-char (or (scan-sexps (point) 1) (buffer-end 1)))
-                   (point))))
-    (if (and delim-beg
-             (> vanilla delim-beg))
-        (tex-parens-forward-list)
-      (goto-char vanilla))))
-
-(defun tex-parens-backward-sexp ()
-  "Internal `backward-sexp' function.
-This function is a wrapper around `backward-sexp' that uses
-tex-parens to identify the previous delimiter.  If `backward-sexp'
-does not take us beyond the ending point of the previous
-delimiter, then do that.  Otherwise, do `tex-parens-backward-list'."
-  (interactive)
-  (let ((delim-end (save-excursion
-                     (when-let ((delim (tex-parens--backward-delim)))
-                       (forward-char (length delim))
-                       (point))))
-        (vanilla (save-excursion
-                   (goto-char (or (scan-sexps (point) -1) (buffer-end -1)))
-                   (backward-prefix-chars)
-                   (point))))
-    (if (and delim-end
-             (< vanilla delim-end))
-        (tex-parens-backward-list)
-      (goto-char vanilla))))
-
-(defvar tex-parens-pairs
-  '(("(" . ")")
-    ("\\Big(" . "\\Big)")
-    ("\\left(" . "\\right)")
-    ("[" . "]")
-    ("\\left[" . "\\right]")
-    ("{" . "}")
-    ("\\{" . "\\}")
-    ("\\left\\{" . "\\right\\}")
-    ("\\langle" . "\\rangle")
-    ("\\left\\langle" . "\\right\\rangle")
-    ("\\lvert" . "\\rvert")
-    ("\\left\\lvert" . "\\right\\rvert")
-    ("\\lVert" . "\\rVert")
-    ("\\left\\lVert" . "\\right\\rVert")
-    ("\\left|" . "\\right|")
-    ("\\left." . "\\right.")
-    ("$" . "$")
-    ("$$" . "$$")
-    ("\\(" . "\\)")
-    ("\\[" . "\\]")
-    ("``" . "''")))
-
-(defun tex-parens-open ()
-  (mapcar #'car tex-parens-pairs))
-
-(defun tex-parens-close ()
-  (mapcar #'cdr tex-parens-pairs))
-
-(defun tex-parens-delims ()
-  (append (tex-parens-open) (tex-parens-close)))
-
-(defun tex-parens-regexp ()
-  (concat (regexp-opt (tex-parens-delims))
-          "\\|\\\\begin{[^}]+}\\|\\\\end{[^}]+}"))
-
-(defun tex-parens-reverse-regexp ()
-  (concat "}[^{]+{nigeb\\\\\\|}[^{]+{dne\\\\\\|"
-          (regexp-opt (mapcar #'reverse (tex-parens-delims)))))
 
 (defcustom tex-parens-search-limit 10000
   "Number of characters to search for a delimiter."
@@ -179,7 +211,7 @@ delimiter, then do that.  Otherwise, do 
`tex-parens-backward-list'."
 (defun tex-parens--forward-delim (&optional bound)
   (interactive)
   (unless bound (setq bound (tex-parens-bound-default-forward)))
-  (when (re-search-forward (tex-parens-regexp) bound t)
+  (when (re-search-forward tex-parens--regexp bound t)
     (match-string 0)))
 
 (defun tex-parens--backward-delim (&optional bound)
@@ -190,29 +222,36 @@ delimiter, then do that.  Otherwise, do 
`tex-parens-backward-list'."
     (with-temp-buffer
       (insert (reverse text))
       (goto-char (point-min))
-      (setq result (re-search-forward (tex-parens-reverse-regexp) nil t))
+      (setq result (re-search-forward tex-parens--regexp-reverse nil t))
       (when result (setq match (match-string 0))))
     (when result
       (backward-char (1- result))
       (reverse match))))
 
-(defun tex-parens-is-open (delim)
+(defun tex-parens--close-of-open (delim)
+  "Check if DELIM is opening, return the corresponding closing.
+If DELIM is an opening delimiter, return the corresponding closing
+delimiter.  Otherwise, return nil."
   (or
-   (cdr (assoc delim tex-parens-pairs))
+   (cdr (assoc delim tex-parens--pairs))
    (and (stringp delim)
         (string-match "\\\\begin{\\([^}]+\\)}" delim)
         (let ((type (match-string 1 delim)))
           (format "\\end{%s}" type)))))
 
-(defun tex-parens-is-close (delim)
+(defun tex-parens--open-of-close (delim)
+  "Check if DELIM is closing, return the corresponding opening.
+If DELIM is a closing delimiter, return the corresponding opening
+delimiter.  Otherwise, return nil."  
   (or
-   (cdr (assoc delim (mapcar (lambda (x) (cons (cdr x) (car x))) 
tex-parens-pairs)))
+   (cdr (assoc delim tex-parens--pairs-swap))
    (and (stringp delim)
         (string-match "\\\\end{\\([^}]+\\)}" delim)
         (let ((type (match-string 1 delim)))
           (format "\\begin{%s}" type)))))
 
-(defun tex-parens-face-mathy ()
+(defun tex-parens--math-face-p ()
+  "Check if point is in a math face."
   (let ((face (plist-get (text-properties-at (point))
                          'face)))
     (or (eq face 'font-latex-math-face)
@@ -232,12 +271,12 @@ delimiter, then do that.  Otherwise, do 
`tex-parens-backward-list'."
       (cond
        ((or
          (and (member delim '("$" "$$"))
-              (tex-parens-face-mathy))
+              (tex-parens--math-face-p))
          (and (not (member delim '("$" "$$")))
-              (tex-parens-is-open delim)))   ; Opening delimiter
+              (tex-parens--close-of-open delim)))   ; Opening delimiter
         (push delim stack))
        (t
-        (let ((other (tex-parens-is-close delim)))
+        (let ((other (tex-parens--open-of-close delim)))
           (cl-assert other)
           ;; (if (equal other (car stack))
           ;;     (pop stack)
@@ -255,10 +294,6 @@ delimiter, then do that.  Otherwise, do 
`tex-parens-backward-list'."
       (when tex-parens--debug
         (message "Unmatched delimiters: %s" (car stack))))))
 
-;; (defun tex-parens-delim-pair (delim)
-;;   (or (assoc delim tex-parens-pairs)
-;;       (assoc delim (mapcar (lambda (x) (cons (cdr x) (car x))) 
tex-parens-pairs))))
-
 (defun tex-parens-backward-list (&optional bound)
   "Find previous TeX sexp. Moves point to start of sexp."
   (interactive)
@@ -272,12 +307,12 @@ delimiter, then do that.  Otherwise, do 
`tex-parens-backward-list'."
          (and (member delim '("$" "$$"))
               (save-excursion
                 (backward-char (length delim))
-                (tex-parens-face-mathy)))
+                (tex-parens--math-face-p)))
          (and (not (member delim '("$" "$$")))
-              (tex-parens-is-close delim)))
+              (tex-parens--open-of-close delim)))
         (push delim stack))
        (t
-        (let ((other (tex-parens-is-open delim)))
+        (let ((other (tex-parens--close-of-open delim)))
           (cl-assert other)
           (if stack
               (progn
@@ -302,6 +337,55 @@ delimiter, then do that.  Otherwise, do 
`tex-parens-backward-list'."
         (message "Unmatched delimiters: %s" (car stack))))))
 
 
+(defun tex-parens-forward-sexp (&optional arg)
+  "Function to use as `forward-sexp-function' in LaTeX-mode."
+  (interactive "^p")
+  (or arg (setq arg 1))
+  (while (> arg 0)
+    (tex-parens-forward-sexp-1)
+    (setq arg (1- arg)))
+  (while (< arg 0)
+    (tex-parens-backward-sexp)
+    (setq arg (1+ arg))))
+
+(defun tex-parens-forward-sexp-1 ()
+  "Internal forward-sexp function.
+This function is a wrapper around `forward-sexp' that uses
+tex-parens to identify the next delimiter.  If `forward-sexp'
+does not take us past the starting point of the next delimiter, then
+do that.  Otherwise, do `tex-parens-forward-list'."
+  (interactive)
+  (let ((delim-beg (save-excursion
+                     (tex-parens--forward-delim)
+                     (match-beginning 0)))
+        (vanilla (save-excursion
+                   (goto-char (or (scan-sexps (point) 1) (buffer-end 1)))
+                   (point))))
+    (if (and delim-beg
+             (> vanilla delim-beg))
+        (tex-parens-forward-list)
+      (goto-char vanilla))))
+
+(defun tex-parens-backward-sexp ()
+  "Internal `backward-sexp' function.
+This function is a wrapper around `backward-sexp' that uses
+tex-parens to identify the previous delimiter.  If `backward-sexp'
+does not take us beyond the ending point of the previous
+delimiter, then do that.  Otherwise, do `tex-parens-backward-list'."
+  (interactive)
+  (let ((delim-end (save-excursion
+                     (when-let ((delim (tex-parens--backward-delim)))
+                       (forward-char (length delim))
+                       (point))))
+        (vanilla (save-excursion
+                   (goto-char (or (scan-sexps (point) -1) (buffer-end -1)))
+                   (backward-prefix-chars)
+                   (point))))
+    (if (and delim-end
+             (< vanilla delim-end))
+        (tex-parens-backward-list)
+      (goto-char vanilla))))
+
 (defun tex-parens-backward-up-list (&optional bound)
   "Find previous TeX sexp. Moves point to start of sexp."
   (interactive)
@@ -316,12 +400,12 @@ delimiter, then do that.  Otherwise, do 
`tex-parens-backward-list'."
          (and (member delim '("$" "$$"))
               (save-excursion
                 (backward-char (length delim))
-                (tex-parens-face-mathy)))
+                (tex-parens--math-face-p)))
          (and (not (member delim '("$" "$$")))
-              (tex-parens-is-close delim)))
+              (tex-parens--open-of-close delim)))
         (push delim stack))
        (t
-        (let ((other (tex-parens-is-open delim)))
+        (let ((other (tex-parens--close-of-open delim)))
           (cl-assert other)
           (if stack
               (progn
@@ -352,25 +436,27 @@ delimiter, then do that.  Otherwise, do 
`tex-parens-backward-list'."
   (interactive)
   (unless bound (setq bound (tex-parens-bound-default-forward)))
   (let ((start (point))
+        success
         (delim (tex-parens--forward-delim bound))
         (stack ()))
     (while delim
       (cond
        ((or
-         (and (equal delim "$")
-              (tex-parens-face-mathy))
-         (and (not (equal delim "$"))
-              (tex-parens-is-open delim)))
+         (and (member delim '("$" "$$"))
+              (tex-parens--math-face-p))
+         (and (not (member delim '("$" "$$")))
+              (tex-parens--close-of-open delim)))
         (push delim stack))
        (t
-        (let ((other (tex-parens-is-close delim)))
+        (let ((other (tex-parens--open-of-close delim)))
           (cl-assert other)
-          (when stack
-            (progn
-              (when tex-parens--debug
-                (unless (equal other (car stack))
-                  (message "Mismatched delimiters: %s %s" delim (car stack))))
-              (pop stack))))
+          (if stack
+              (progn
+                (when tex-parens--debug
+                  (unless (equal other (car stack))
+                    (message "Mismatched delimiters: %s %s" delim (car 
stack))))
+                (pop stack))
+            (setq success t)))
         ;; (cdr (tex-parens-delim-pair delim))
                                         ; Closing delimiter
         ;; (if (equal (cdr (tex-parens-delim-pair delim)) (car stack))
@@ -378,10 +464,60 @@ delimiter, then do that.  Otherwise, do 
`tex-parens-backward-list'."
         ;;   (setq success t))
         )
        )
-      (setq delim (and stack (tex-parens--forward-delim bound))))
+      (setq delim (and (not success) (tex-parens--forward-delim bound))))
     (unless success
       (goto-char start))))
 
+
+(defun tex-parens-forward-sexp (&optional arg)
+  "Function to use as `forward-sexp-function' in LaTeX-mode."
+  (interactive "^p")
+  (or arg (setq arg 1))
+  (while (> arg 0)
+    (tex-parens-forward-sexp-1)
+    (setq arg (1- arg)))
+  (while (< arg 0)
+    (tex-parens-backward-sexp)
+    (setq arg (1+ arg))))
+
+(defun tex-parens-forward-sexp-1 ()
+  "Internal forward-sexp function.
+This function is a wrapper around `forward-sexp' that uses
+tex-parens to identify the next delimiter.  If `forward-sexp'
+does not take us past the starting point of the next delimiter, then
+do that.  Otherwise, do `tex-parens-forward-list'."
+  (interactive)
+  (let ((delim-beg (save-excursion
+                     (tex-parens--forward-delim)
+                     (match-beginning 0)))
+        (vanilla (save-excursion
+                   (goto-char (or (scan-sexps (point) 1) (buffer-end 1)))
+                   (point))))
+    (if (and delim-beg
+             (> vanilla delim-beg))
+        (tex-parens-forward-list)
+      (goto-char vanilla))))
+
+(defun tex-parens-backward-sexp ()
+  "Internal `backward-sexp' function.
+This function is a wrapper around `backward-sexp' that uses
+tex-parens to identify the previous delimiter.  If `backward-sexp'
+does not take us beyond the ending point of the previous
+delimiter, then do that.  Otherwise, do `tex-parens-backward-list'."
+  (interactive)
+  (let ((delim-end (save-excursion
+                     (when-let ((delim (tex-parens--backward-delim)))
+                       (forward-char (length delim))
+                       (point))))
+        (vanilla (save-excursion
+                   (goto-char (or (scan-sexps (point) -1) (buffer-end -1)))
+                   (backward-prefix-chars)
+                   (point))))
+    (if (and delim-end
+             (< vanilla delim-end))
+        (tex-parens-backward-list)
+      (goto-char vanilla))))
+
 (defun tex-parens-down-list (&optional bound)
   (interactive)
   (unless bound (setq bound (tex-parens-bound-default-forward)))
@@ -391,9 +527,9 @@ delimiter, then do that.  Otherwise, do 
`tex-parens-backward-list'."
     (when (and delim
                (or
                 (and (equal delim "$")
-                     (tex-parens-face-mathy))
+                     (tex-parens--math-face-p))
                 (and (not (equal delim "$"))
-                     (tex-parens-is-open delim))))
+                     (tex-parens--close-of-open delim))))
       (setq success t))
     (unless success
       (goto-char start))

Reply via email to