branch: externals/tramp-hlo
commit e1269f11511db0f8b6b27435e55a65d32cbfb92b
Author: Joe Sadusk <[email protected]>
Commit: Joe Sadusk <[email protected]>

    Fixed tramp-hlo-dir-locals-find-file to use a larger script and do 
everything in a single roundtrip, and made use of tramp-add-external-operation
---
 tramp-hlo.el | 259 ++++++++++++++++++++++++++++++++++++++---------------------
 1 file changed, 167 insertions(+), 92 deletions(-)

diff --git a/tramp-hlo.el b/tramp-hlo.el
index 470d4d88b9..67933d893d 100644
--- a/tramp-hlo.el
+++ b/tramp-hlo.el
@@ -110,26 +110,78 @@ echo \\)
   "Script to find several dominating files on a remote host"
 )
 
-(defmacro tramp-hlo-advice (file fallback &rest body)
-  "Setup macro for `tramp-hlo' advice functions.
-Creates a scaffold to make an advice function to add a tramp implementation to
-a high level function. FILE is a filename that might be a remote file with a
-tramp-sh backend. If so, execute the BODY of the macro, otherwise run the
-FALLBACK expression. If executing BODY, the dissected FILE will be in scope as
-`vec'."
-  `(if-let* ((non-essential t)
-            (vec (ignore-errors
-                   (tramp-dissect-file-name (expand-file-name ,file))))
-            ((tramp-sh-file-name-handler-p vec))
-             (non-essential nil)
-             )
-       (progn ,@body)
-       (,@fallback)
-     )
-  )
+(defconst tramp-hlo-dir-locals-find-file-cache-update-script
+  "
+FILE=$1
+shift
+NAMES=$1
+shift
+CACHEDIRS=$@
+STAT_FORMAT=\"%%Y\"
+STAT_FORMAT=\"${STAT_FORMAT#?}\"
+if [ -e \"$FILE\" ]; then
+    FILE=\"$(realpath $FILE)\"
+    TEST=\"$(dirname $FILE )\"
+    echo \"(\"
+    echo \":file \\\"$FILE\\\" \"
+    FOUND=\"\"
+    while [ ! -z \"$TEST\" ] && [ -z \"$FOUND\" ]; do
+        for NAME in $NAMES; do
+            if [ -f \"$TEST/$NAME\" ]; then
+                DOMINATING_DIR=\"$TEST\"
+                MTIME=\"$(stat -c \"$STAT_FORMAT\" \"$TEST/$NAME\")\"
+                FOUND=\"$FOUND ( \\\"$NAME\\\" . $MTIME ) \"
+            fi
+        done
+        if [ -z \"$FOUND\" ]; then
+            if [ \"$TEST\" = \"/\" ]; then
+                TEST=\"\"
+            else
+                TEST=\"${TEST%/*}\"
+                if [ -z \"$TEST\" ]; then
+                    TEST=\"/\"
+                fi
+            fi
+        fi
+    done
+
+    if [ ! -z \"$FOUND\" ]; then
+        echo \":locals  (\\\"$DOMINATING_DIR\\\" $FOUND )\"
+    fi
+
+    DOMINATING_DIR_LEN=$(expr length \"$DOMINATING_DIR\")
+    FOUND_CACHEDIR=\"\"
+    FOUND_CACHEDIR_LEN=0
+    for CACHEDIR in $CACHEDIRS; do
+        CACHEDIR_LEN=$(expr length \"$CACHEDIR\")
+
+        if [ -d \"$CACHEDIR\" ] && [ \"$CACHEDIR_LEN\" -gt 
\"$FOUND_CACHEDIR_LEN\" ] && [ \"${FILE#$CACHEDIR}\" != \"$FILE\" ]; then
+            FOUND_CACHEDIR=$CACHEDIR
+            FOUND_CACHEDIR_LEN=$CACHEDIR_LEN
+        fi
+    done
+
+    if [ ! -z \"$FOUND_CACHEDIR\" ]; then
+        echo \":cache ( \\\"$FOUND_CACHEDIR\\\" \"
+        for NAME in $NAMES; do
+            if [ -f \"$FOUND_CACHEDIR/$NAME\" ]; then
+                MTIME=\"$(stat -c \"$STAT_FORMAT\" \"$FOUND_CACHEDIR/$NAME\")\"
+                echo \"( \\\"$NAME\\\" . $MTIME ) \"
+            fi
+        done
+        echo \")\"
+    fi
+
+    echo \")\"
+    return 0
+else
+    return 1
+fi"
+  "Support script for `dir-locals-find-file'"
+)
 
 
-(defun tramp-hlo-dir-locals--all-files (orig-fun directory &optional 
base-el-only)
+(defun tramp-hlo-dir-locals--all-files (directory &optional base-el-only)
   "Tramp optimized version of `dir-locals--all-files'.
 Return a list of all readable dir-locals files in the directory
 represented by VEC.
@@ -137,10 +189,8 @@ The returned list is sorted by increasing priority.  That 
is,
 values specified in the last file should take precedence over
 those in the first.
 
-The optional argument BASE-EL-ONLY will only consider the base dir locals file.
-This is intended as an advice function, with ORIG-FUN as the fallback 
function."
-  (tramp-hlo-advice directory
-    (funcall orig-fun directory)
+The optional argument BASE-EL-ONLY will only consider the base dir locals 
file."
+  (with-parsed-tramp-file-name directory vec
     (let* ((localdir (directory-file-name (tramp-file-name-localname vec)))
            (file-1 dir-locals-file)
            (file-2 (when (string-match "\\.el\\'" file-1)
@@ -199,7 +249,7 @@ if not found."
     )
   )
 
-(defun tramp-hlo-locate-dominating-file (orig-fun file name)
+(defun tramp-hlo-locate-dominating-file (file name)
   "Tramp version of `locate-dominating-file'
 Starting at FILE, look up directory hierarchy for directory containing NAME.
 FILE can be a file or a directory.  If it's a file, its directory will
@@ -209,10 +259,8 @@ and return the directory.  Return nil if not found.
 Instead of a string, NAME can also be a predicate taking one argument
 \(a directory) and returning a non-nil value if that directory is the one for
 which we're looking.  The predicate will be called with every file/directory
-the function needs to examine, starting with FILE.
-This is intended as an advice function, with ORIG-FUN as the fallback 
function."
-  (tramp-hlo-advice file
-   (funcall orig-fun file name)
+the function needs to examine, starting with FILE."
+  (with-parsed-tramp-file-name file vec
    (if (functionp name)
        (tramp-hlo-locate-dominating-file-pred vec name)
      (let* ((names (if (listp name) name (list name)))
@@ -226,7 +274,46 @@ This is intended as an advice function, with ORIG-FUN as 
the fallback function."
    )
   )
 
-(defun tramp-hlo-dir-locals-find-file (orig-fun file)
+(defun tramp-hlo-dir-locals-find-file-cache-update (file cache)
+  "Prepare inputs and run support script for `tramp-hlo-dir-locals-find-file'
+Perform the equivalent of `expand-file-name', `locate-dominating-file' and
+`file-attribute-modification-time' in one tramp operation.
+The operations performed are:
+  - Expand the filename of FILE
+  - Locate the dominating directory-locals files for the directory containing
+    FILE
+  - Resolve the mtime for all directory-locals files located
+  - Find cache directories from CACHE which is equivalent to
+    `dir-locals-directory-cache' that are on the same remote as FILE
+  - Find the highest level cache directory located under the expanded path of
+    FILE, if it exists
+  - Resolve the mtime of all directory-locals files under the cache directory
+
+This function returns a plist with the fields:
+  - `:file' containing the expanded filename of FILE
+  - `:locals' containing a list with the located dir-locals directory under 
FILE
+    and a dotted pair list of dir-locals files under that directory with their
+    mtime
+  - `:cache' containing the most appropriate cache directory under FILE with a
+    dotted pair list of dir-locals file and mtime
+`:locals' and `:cache' are optional fields, and are missing if not found."
+  (with-parsed-tramp-file-name file vec
+    (tramp-maybe-send-script vec 
tramp-hlo-dir-locals-find-file-cache-update-script 
"dir_locals_find_file_cache_update")
+    (let* (
+           (file-connection (file-remote-p file))
+           (cache-dirs (mapcar #'car cache))
+           (cache-remotes (mapcar 'file-remote-p cache-dirs))
+           (same-remote-cache-dirs (seq-filter (lambda (cache-dir) (string= 
file-connection (file-remote-p cache-dir))) cache-dirs))
+           (same-remotes (mapcar (lambda (cache-dir) (string= file-connection 
(file-remote-p cache-dir))) cache-dirs))
+           (cache-dirs-local (mapcar #'file-local-name same-remote-cache-dirs))
+           (cache-dirs-quoted (mapcar #'tramp-shell-quote-argument 
cache-dirs-local))
+           (cache-dirs-string (string-join cache-dirs-quoted " "))
+           (command (format "dir_locals_find_file_cache_update %s 
\".dir-locals.el .dir-locals2.el\" %s" (tramp-file-local-name file) 
cache-dirs-string))
+           )
+      (tramp-send-command-and-read vec command))))
+
+
+(defun tramp-hlo-dir-locals-find-file (file)
   "Tramp implementation of `dir-locals-find-file'.
 Find the directory-local variables for FILE.
 This searches upward in the directory tree from FILE.
@@ -245,86 +332,74 @@ This function returns either:
   - the matching entry from `dir-locals-directory-cache' (a list),
   - or the full path to the directory (a string) containing at
     least one `dir-locals-file' in the case of no valid cache
-    entry.
+    entry."
+  (let* (
+         (file-connection (file-remote-p file))
+         (cache-update (tramp-hlo-dir-locals-find-file-cache-update file 
dir-locals-directory-cache))
+         (file (concat file-connection (plist-get cache-update :file)))
+         (locals-dir-update (plist-get cache-update :locals))
+         (locals-dir (if locals-dir-update (concat file-connection (car 
locals-dir-update))))
+         (cache-dir-update (plist-get cache-update :cache))
+         (cache-dir (if cache-dir-update (concat file-connection (car 
cache-dir-update))))
+         (dir-elt (if cache-dir-update (seq-find (lambda (elt) (string= (car 
elt) cache-dir)) dir-locals-directory-cache)))
+         )
+    (if (and dir-elt
+             (or (null locals-dir)
+                 (<= (length locals-dir)
+                     (length (car dir-elt)))))
+        ;; Found a potential cache entry.  Check validity.
+        ;; A cache entry with no MTIME is assumed to always be valid
+        ;; (ie, set directly, not from a dir-locals file).
+        ;; Note, we don't bother to check that there is a matching class
+        ;; element in dir-locals-class-alist, since that's done by
+        ;; dir-locals-set-directory-class.
+        (if (or (null (nth 2 dir-elt))
+                (let ((cached-files (cdr cache-dir-update)))
+                  ;; The entry MTIME should match the most recent
+                  ;; MTIME among matching files.
+                  (and cached-files
+                      (time-equal-p
+                       (nth 2 dir-elt)
+                       (let ((latest 0))
+                         (dolist (f cached-files latest)
+                           (let ((f-time
+                                  (seconds-to-time (cdr f))))
+                             (if (time-less-p latest f-time)
+                                 (setq latest f-time)))))))))
+            ;; This cache entry is OK.
+            dir-elt
+          ;; This cache entry is invalid; clear it.
+          (setq dir-locals-directory-cache
+                (delq dir-elt dir-locals-directory-cache))
+          ;; Return the first existing dir-locals file.  Might be the same
+          ;; as dir-elt's, might not (eg latter might have been deleted).
+          locals-dir)
+      ;; No cache entry.
+      locals-dir)))
 
-This is intended as an advice function, with ORIG-FUN as the fallback 
function."
-  (tramp-hlo-advice file
-      (funcall orig-fun file)
-      (let* ((locals-files (tramp-hlo-locate-dominating-file-list
-                            vec
-                            '(".dir-locals.el" ".dir-locals-2.el")))
-              (locals-dir (if locals-files (file-name-directory (car 
locals-files)) nil))
-              dir-elt)
-         ;; `locate-dominating-file' may have abbreviated the name.
-         (when locals-dir
-           (setq locals-dir (expand-file-name locals-dir)))
-         ;; Find the best cached value in `dir-locals-directory-cache'.
-         (dolist (elt dir-locals-directory-cache)
-           (when (and (string-prefix-p (car elt) file
-                                       (memq system-type
-                                             '(windows-nt cygwin ms-dos)))
-                      (> (length (car elt)) (length (car dir-elt))))
-             (setq dir-elt elt)))
-         (if (and dir-elt
-                  (or (null locals-dir)
-                      (<= (length locals-dir)
-                          (length (car dir-elt)))))
-             ;; Found a potential cache entry.  Check validity.
-             ;; A cache entry with no MTIME is assumed to always be valid
-             ;; (ie, set directly, not from a dir-locals file).
-             ;; Note, we don't bother to check that there is a matching class
-             ;; element in dir-locals-class-alist, since that's done by
-             ;; dir-locals-set-directory-class.
-             (if (or (null (nth 2 dir-elt))
-                     (let ((cached-files (dir-locals--all-files (car 
dir-elt))))
-                       ;; The entry MTIME should match the most recent
-                       ;; MTIME among matching files.
-                       (and cached-files
-                           (time-equal-p
-                            (nth 2 dir-elt)
-                            (let ((latest 0))
-                              (dolist (f cached-files latest)
-                                (let ((f-time
-                                       (file-attribute-modification-time
-                                        (file-attributes f))))
-                                  (if (time-less-p latest f-time)
-                                      (setq latest f-time)))))))))
-                 ;; This cache entry is OK.
-                 dir-elt
-               ;; This cache entry is invalid; clear it.
-               (setq dir-locals-directory-cache
-                     (delq dir-elt dir-locals-directory-cache))
-               ;; Return the first existing dir-locals file.  Might be the same
-               ;; as dir-elt's, might not (eg latter might have been deleted).
-               locals-dir)
-           ;; No cache entry.
-           locals-dir))
-      )
-  )
 
 (defun setup-tramp-hlo ()
   "Setup tramp high-level functions.
-Adds advice functions with tramp implementations for the following emacs
-built-in functions:
+Adds tramp external operations for the following emacs built-in functions:
 - `dir-locals--all-files'
 - `locate-dominating-file'
 - `dir-locals-find-file'"
   (interactive)
-  (advice-add 'dir-locals--all-files :around #'tramp-hlo-dir-locals--all-files)
-  (advice-add 'locate-dominating-file :around 
#'tramp-hlo-locate-dominating-file)
-  (advice-add 'dir-locals-find-file :around #'tramp-hlo-dir-locals-find-file)
+  (tramp-add-external-operation 'dir-locals--all-files 
#'tramp-hlo-dir-locals--all-files 'tramp-sh)
+  (tramp-add-external-operation 'locate-dominating-file 
#'tramp-hlo-locate-dominating-file 'tramp-sh)
+  (tramp-add-external-operation 'dir-locals-find-file 
#'tramp-hlo-dir-locals-find-file 'tramp-sh)
   )
 
 (defun remove-tramp-hlo ()
   "Remove tramp high-level functions.
-Remove tramp advice functions for the following emacs built-in functions:
+Remove tramp external operations for the following emacs built-in functions:
 - `dir-locals--all-files'
 - `locate-dominating-file'
 - `dir-locals-find-file'"
   (interactive)
-  (advice-remove 'dir-locals--all-files #'tramp-hlo-dir-locals--all-files)
-  (advice-remove 'locate-dominating-file #'tramp-hlo-locate-dominating-file)
-  (advice-remove 'dir-locals-find-file #'tramp-hlo-dir-locals-find-file)
+  (tramp-remove-external-operation 'dir-locals--all-files 'tramp-sh)
+  (tramp-remove-external-operation 'locate-dominating-file 'tramp-sh)
+  (tramp-remove-external-operation 'dir-locals-find-file 'tramp-sh)
   )
 
 (provide 'tramp-hlo)

Reply via email to