auctex-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

feature/capf 83eddfa3: latex-capf.el: Make it work


From: Arash Esbati
Subject: feature/capf 83eddfa3: latex-capf.el: Make it work
Date: Fri, 14 Oct 2022 06:32:24 -0400 (EDT)

branch: feature/capf
commit 83eddfa38ef073d78b6808cf4e79f50fa970ffaf
Author: Arash Esbati <arash@gnu.org>
Commit: Arash Esbati <arash@gnu.org>

    latex-capf.el: Make it work
---
 latex-capf.el | 313 +++++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 221 insertions(+), 92 deletions(-)

diff --git a/latex-capf.el b/latex-capf.el
index 9c8af3b8..aca92127 100644
--- a/latex-capf.el
+++ b/latex-capf.el
@@ -22,58 +22,81 @@ several lines in TeX."
   "Find out if point is within the arguments of any TeX-macro.
 The return value is
 
-  (\"name\" mac-or-env integer type)
+  (\"name\" mac-or-env total-num type opt-num opt-distance)
 
 \"name\" is the name of the macro (without backslash) or
   environment as a string.
 mac-or-env is one of the symbols `mac' or `env'.
-integer is the number of the argument where we started.
+total-num is the total number of the argument before the point started.
 type is one of the symbols `mandatory' or `optional'.
+opt-num is the number of optional arguments before the point started.
+opt-distance the number of optional arguments after the last mandatory.
 
-If the optional BOUND is an integer, bound backwards directed
-searches to this point.  If it is nil, limit to the previous 15
-lines."
-  ;; Exit if we're not inside a list or inside a nested list:
-  (unless (/= (car (syntax-ppss)) 1)
-    (let ((bound (or bound (line-beginning-position -15)))
-          (env-or-mac 'mac)
-          cmd cnt cnt-opt type)
+If the optional BOUND is an integer, limit backward searches to
+this point.  If nil, limit to the previous 15 lines."
+  (let ((bound (or bound (line-beginning-position -15)))
+        (env-or-mac 'mac)
+        cmd cnt cnt-opt type result ;; env-or-mac-start
+        (cnt-distance 0))
+    (save-excursion
+      (save-restriction
+        (narrow-to-region (max (point-min) bound) (point-max))
+        ;; Move back out of the current parenthesis
+        (let ((forward-sexp-function nil))
+          (up-list -1))
+        ;; Set the initial value of argument counter
+        (setq cnt 1)
+        ;; Note that we count also the right opt. or man. arg:
+        (setq cnt-opt (if (= (following-char) ?\{) 0 1))
+        ;; Record if we're inside a mand. or opt. argument
+        (setq type (if (= (following-char) ?\{) 'mandatory 'optional))
+        ;; Move back over any touching sexps
+        (while (and (TeX-move-to-previous-arg bound)
+                    (condition-case nil
+                        (let ((forward-sexp-function nil))
+                          (backward-sexp) t)
+                      (error nil)))
+          (when (eq (following-char) ?\[)
+            (cl-incf cnt-opt))
+          (cl-incf cnt))
+        ;; (setq env-or-mac-start (point))
+        (when (and (or (= (following-char) ?\[)
+                       (= (following-char) ?\{))
+                   (re-search-backward "\\\\[*a-zA-Z]+\\=" nil t))
+          (setq cmd (TeX-match-buffer 0))
+          (when (looking-at "\\\\begin{\\([^}]+\\)}")
+            (setq cmd (TeX-match-buffer 1))
+            (setq env-or-mac 'env)
+            (cl-decf cnt))
+          (when (and cmd (not (string= cmd "")))
+            (setq result (list (if (eq env-or-mac 'mac)
+                                   ;; Strip leading backslash from
+                                   ;; the macro
+                                   (substring cmd 1)
+                                 cmd)
+                               env-or-mac cnt type cnt-opt))))))
+    ;; If we were inside an optional argument after a mandatory one,
+    ;; we have to find out the number of optional arguments before
+    ;; the mandatory one.
+    (when (and (eq (nth 3 result) 'optional)
+               (/= 0 (- (nth 2 result) (nth 4 result))))
       (save-excursion
         (save-restriction
           (narrow-to-region (max (point-min) bound) (point-max))
-          ;; Move back out of the current parenthesis
           (let ((forward-sexp-function nil))
             (up-list -1))
-          ;; Set the initial value of argument counter
-          (setq cnt 1)
-          ;; Note that we count also the right opt. or man. arg:
-          (setq cnt-opt (if (= (following-char) ?\{) 0 1))
-          ;; Record if we're inside a mand. or opt. argument
-          (setq type (if (= (following-char) ?\{) 'mandatory 'optional))
-          ;; Move back over any touching sexps
+          (unless (= (following-char) ?\{)
+            (cl-incf cnt-distance))
           (while (and (TeX-move-to-previous-arg bound)
                       (condition-case nil
                           (let ((forward-sexp-function nil))
-                            (backward-sexp) t)
+                            (backward-sexp)
+                            (/= (following-char) ?\{))
                         (error nil)))
-            (if (eq (following-char) ?\[) (cl-incf cnt-opt))
-            (cl-incf cnt))
-          (when (and (or (= (following-char) ?\[)
-                         (= (following-char) ?\{))
-                     (re-search-backward "\\\\[*a-zA-Z]+\\=" nil t))
-            (setq cmd (TeX-match-buffer 0))
-            (when (looking-at "\\\\begin{\\([^}]+\\)}")
-              (setq cmd (TeX-match-buffer 1))
-              (setq env-or-mac 'env)
-              (cl-decf cnt))
-            (if (and cmd (not (string= cmd "")))
-                (list (if (eq env-or-mac 'mac)
-                          ;; Strip leading backslash from
-                          ;; the macro
-                          (substring cmd 1)
-                        cmd)
-                      env-or-mac cnt type)
-              nil)))))))
+            (cl-incf cnt-distance)))))
+    ;; Check if we really have a result before adding something new:
+    (when result
+      (append result (list cnt-distance)))))
 
 (defun LaTeX-completion-candidates-key-val (key-vals)
   ;; First find out if we're looking for a key or a value: If we're
@@ -130,74 +153,180 @@ lines."
                        (lambda (_)
                          (funcall func key-vals))))))))
 
+(defun LaTeX-completion-candidates-completing-read-multiple (collection)
+  (let ((end (point))
+        beg list-beg)
+    (save-excursion
+      (up-list -1)
+      (setq list-beg (1+ (point))))
+    (save-excursion
+      (unless (search-backward "," list-beg t)
+        (goto-char list-beg))
+      (skip-chars-forward "^a-zA-Z0-9" end)
+      (setq beg (point)))
+    (list beg end (completion-table-dynamic
+                   (lambda (_)
+                     collection)))))
+
 (defun LaTeX-completion-candidates-completing-read (collection)
   (let ((end (point))
-        beg key)  ))
+        beg)
+    (save-excursion
+      (up-list -1)
+      (forward-char)
+      (skip-chars-forward "^a-zA-Z0-9" end)
+      (setq beg (point)))
+    (list beg end (completion-table-dynamic
+                   (lambda (_)
+                     collection)))))
 
-(defun LaTeX-completion-candidates-completing-read-multiple (table)
-  nil)
+(defun LaTeX-completion-parse-args (entry)
+  (let* ((name (nth 0 entry))
+         (mac-or-env (nth 1 entry))
+         (total-num (nth 2 entry))
+         (type (nth 3 entry))
+         (opt-num (nth 4 entry))
+         (opt-dis (nth 5 entry))
+         (mand-num (- total-num opt-num))
+         (cnt 0)
+         (again t)
+         arg-list result)
+    (setq arg-list (cdr (assoc name (if (eq mac-or-env 'mac)
+                                        (TeX-symbol-list)
+                                      (LaTeX-environment-list)))))
+    ;; FIXME: Check for `TeX-arg-conditional' here and change
+    ;; `arg-list' accordingly
+    ;;
+    ;; Check if there is a `LaTeX-env-args' in the `arg-list' and
+    ;; remove it:
+    (when (and (eq mac-or-env 'env)
+               (eq (car arg-list) #'LaTeX-env-args))
+      (pop arg-list))
+    (cond ((and (eq type 'optional)
+                (= opt-dis 0))
+           ;; Optional arg without mandatory one before: This case is
+           ;; straight and we just pick the correct one out of the
+           ;; list:
+           (setq result (nth (1- total-num) arg-list)))
+          ;; Mandatory arg: Loop over the arg-list and drop all
+          ;; vectors at the list beginning:
+          ((eq type 'mandatory)
+           (while (vectorp (car arg-list))
+             (pop arg-list))
+           ;; The next entry must be a mandatory arg.  If we're
+           ;; looking for the first mandatory argument, just pick the
+           ;; first element.  Otherwise loop further over the list and
+           ;; count for the correct arg:
+           (if (= mand-num 1)
+               (setq result (car arg-list))
+             (while again
+               (cond ((vectorp (car arg-list))
+                      (pop arg-list)
+                      (setq again t))
+                     ((= (cl-incf cnt) mand-num)
+                      (setq again nil)
+                      (setq result (car arg-list)))
+                     (t
+                      ;; Be a little conservative against infloops.
+                      (if arg-list
+                          (progn (setq again t)
+                                 (pop arg-list))
+                        (setq again nil)))))))
+          ;; Optional arg after mandatory one(s): This isn't fun :-(
+          ((and (eq type 'optional)
+                (/= opt-dis 0))
+           (setq again t)
+           (setq cnt 0)
+           ;; The idea is: Look for non-vectors and count the number
+           ;; of mandatory argument in `mand-num'.
+           (while again
+             (cond ((and (not (vectorp (car arg-list)))
+                         (/= (cl-incf cnt) mand-num))
+                    (pop arg-list)
+                    (setq again t))
+                   ((and (not (vectorp (car arg-list)))
+                         ;; Don't incf mand-num again; is done in the
+                         ;; clause above:
+                         (= cnt mand-num))
+                    (setq again nil))
+                   ;; If the clauses above fail, we can safely drop
+                   ;; vectors:
+                   ((vectorp (car arg-list))
+                    (pop arg-list)
+                    (setq again t))
+                   (t
+                    (setq again nil))))
+           (setq result (nth opt-dis arg-list)))
+          (t nil))
+    result))
 
 (defun LaTeX-completion-parse-arg (arg)
   "Prepare ARG suitable for further completion processing."
-  (when (and (or (vectorp arg)
-                 (listp arg))
-             (symbolp (elt arg 0))
-             (fboundp (elt arg 0)))
+  (when (or (and (vectorp arg)
+                 (fboundp (elt arg 0)))
+            (and (listp arg)
+                 (fboundp (car arg)))
+            (and (symbolp arg)
+                 (fboundp arg)))
     ;; Turn a vector into a list:
     (when (vectorp arg)
       (setq arg (append arg nil)))
-    (let ((head (car arg))
-          (tail (cadr arg)))
+    ;; Turn a single function symbol into a list:
+    (unless (listp arg)
+      (setq arg (list arg)))
+    (let* ((head (car arg))
+           (tail (cadr arg))
+           (fun1 (lambda (elt)
+                   (cond ((symbolp elt)
+                          ;; It is a variable name
+                          (symbol-value elt))
+                         ;; It is a function call ...
+                         ((and (listp elt)
+                               (symbolp (car elt))
+                               (fboundp (car elt)))
+                          ;; ... w/ or w/o args:
+                          (if (= (length elt) 1)
+                              (funcall (car elt))
+                            (eval elt t)))
+                         ;; It is a plain list of strings:
+                         (t elt)))))
       (cond ((eq head #'TeX-arg-key-val)
              (LaTeX-completion-candidates-key-val
-              (cond ((symbolp tail)
-                     ;; It is a variable name
-                     (symbol-value tail))
-                    ;; It is a function call ...
-                    ((and (listp tail)
-                          (symbolp (car tail))
-                          (fboundp (car tail)))
-                     ;; ... w/ or w/o args:
-                     (if (= (length tail) 1)
-                         (funcall (car tail))
-                       (eval tail t)))
-                    ;; It is a plain list of strings:
-                    (t tail))))
-            )  )))
+              (funcall fun1 tail)))
+            ((eq head #'TeX-arg-completing-read-multiple)
+             (LaTeX-completion-candidates-completing-read-multiple
+              (funcall fun1 tail)))
+            ((eq head #'TeX-arg-completing-read)
+             (LaTeX-completion-candidates-completing-read
+              (funcall fun1 tail)))
+            ((assq (car arg) LaTeX-completion-function-map-alist)
+             (funcall (cdr (assq (car arg) 
LaTeX-completion-function-map-alist))))
+            (t nil)))))
+
+(defvar LaTeX-completion-function-map-alist
+  '((TeX-arg-counter . LaTeX-counter-list)
+    (TeX-arg-pagestyle . LaTeX-pagestyle-list)
+    ;; ...
+    )
+  "Alist mapping inserting funcs to their completion-candidates counterparts.
+Each element is a cons with the name of the function which
+queries and inserts something in the buffer as car and the name
+function delievering completion candidates as cdr.")
 
 (defun LaTeX-arguments-completion-at-point ()
   ;; Exit if not inside a list or a nested list or in a comment:
-  (unless (or (/= (car (syntax-ppss)) 1)
-              (TeX-in-comment))
-    (let ((entry (LaTeX-what-macro))
-          mac env arg)
-      (cond ((eq (nth 1 entry) 'mac)
-             ;; Feed 'arg' to `LaTeX-completion-parse-arg' only when
-             ;; we find one:
-             (and (setq mac (assoc (car entry) (TeX-symbol-list)))
-                  (setq arg (nth (nth 2 entry) mac))
-                  (LaTeX-completion-parse-arg arg)))
-            ((eq (nth 1 entry) 'env)
-             ;; Feed 'arg' to `LaTeX-completion-parse-arg' only when
-             ;; we find one:
-             (and (setq env (assoc (car entry) (LaTeX-environment-list)))
-                  ;; Shift the number of arg if `LaTeX-env-args' is present:
-                  (setq arg (if (eq (cadr env) 'LaTeX-env-args)
-                                (nth (1+ (nth 2 entry)) env)
-                              (nth (nth 2 entry) env)))
-                  (LaTeX-completion-parse-arg arg)))
+  (when (or (/= (car (syntax-ppss)) 0)
+            (not (TeX-in-comment)))
+    (let ((entry (LaTeX-what-macro)))
+      (cond ((or (and (eq (nth 1 entry) 'mac)
+                      (assoc (car entry) (TeX-symbol-list)))
+                 (and (eq (nth 1 entry) 'env)
+                      (assoc (car entry) (LaTeX-environment-list))))
+             ;; Check if we find the macro name inside
+             ;; `TeX-symbol-list' or the env name inside
+             ;; `LaTeX-environment-list' otherwise don't start the
+             ;; procedure:
+             (LaTeX-completion-parse-arg
+              (LaTeX-completion-parse-args entry)))
             ;; Any other constructs?
             (t nil)))))
-
-;; (defun LaTeX-completion-candidates-single (func)
-
-;;   (let ((end (point))
-;;         beg)
-;;     (cond ((eq func #'TeX-arg-pagestyle)
-;;            (save-excursion
-;;              (skip-chars-backward "a-zA-Z0-9")
-;;              (setq beg (point)))
-;;            (list beg end (completion-table-dynamic
-;;                           (lambda (_) (LaTeX-pagestyle-list)))))
-;;           (t nil))
-;;     ))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]