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

    First working version of tramp optimized versions of
    dir-locals--all-files, locate-dominating-file, and dir-locals-find-file
---
 tramp-hlo.el | 262 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 262 insertions(+)

diff --git a/tramp-hlo.el b/tramp-hlo.el
new file mode 100644
index 0000000000..52992b5c73
--- /dev/null
+++ b/tramp-hlo.el
@@ -0,0 +1,262 @@
+;; -*- lexical-binding: t -*-
+
+;;; tramp-hlo.el --- High level operations as tramp handlers
+;; Author Joe Sadusk <[email protected]>
+;; Version 0.0.1
+
+;;; Commentary
+;; This is an attempt to optimize tramp remote editing with slow
+;; connection by building higher level core lisp functions as tramp
+;; operations. The idea is to reduce round trips by doing more on the
+;; server in one request.
+
+;;; Code
+
+(defgroup tramp-hlo nil
+  "High level operations as tramp handlers"
+  :group 'tools)
+
+(require 'tramp-sh)
+
+(defconst tramp-hlo-dir-locals--all-files-script
+  "
+DIR=$1
+FILE1=$2
+FILE2=$3
+if [ ! -d \"$DIR\" ]; then
+    echo nil
+else
+    cd \"$DIR\"
+    echo \\(
+    for FILE in \"$FILE1\" \"$FILE2\"; do
+        if [ -r \"$FILE\" ] && [ -f \"$FILE\" ] && [ ! -d \"$FILE\" ]; then
+            echo \"\\\"$DIR/$FILE\\\"\"
+    fi
+    done
+    echo \\)
+fi
+"
+  "Script to check for dir-locals in a remote dir"
+  )
+
+(defconst tramp-hlo-list-parents-script
+  "
+FILE=$1
+TEST=\"$(dirname $FILE )\"
+if [ ! -d \"$TEST\" ]; then
+    echo nil
+else
+    echo \\(
+    while [ \"$TEST\" != \"\" ]; do
+        echo \"\\\"$TEST/\\\"\" | sed \"s|^$HOME|~|\"
+        TEST=${TEST%/*}
+    done
+    echo \\\"/\\\"
+    echo \\)
+fi
+"
+  "Script to list all parents in upward order of a directory, with home 
abbreviations"
+  )
+
+(defconst tramp-hlo-locate-dominating-file-script
+  "
+FILE=$1
+NAME=$2
+TEST=\"$(dirname $FILE )\"
+if [ ! -d \"$TEST\" ]; then
+    echo nil
+else
+    while [ ! -z \"$TEST\" ] && [ ! -e \"$TEST/$NAME\" ]; do
+        TEST=${TEST\%/*}
+    done
+    if [ -f \"$TEST/$NAME\" ]; then
+        echo -n \"\\\"$TEST/\\\"\" | sed \"s|^\\\"$HOME|\\\"~|\"
+    elif [ -f \"/$NAME\" ]; then
+        echo -n \\\"/\\\"
+    else
+        echo nil
+    fi
+fi
+"
+  "Script to find a dominating file directory on a remote host"
+  )
+
+(defconst tramp-hlo-locate-dominating-file-multi-script
+  "
+FILE=$1
+shift
+NAMES=$@
+TEST=\"$(dirname $FILE )\"
+echo \\(
+if [ -d \"$TEST\" ]; then
+    FOUND=\"\"
+    while [ ! -z \"$TEST\" ] && [ -z \"$FOUND\" ]; do
+        for NAME in $NAMES; do
+            if [ -f \"$TEST/$NAME\" ]; then
+                echo \"\\\"$TEST/$NAME\\\"\"
+                FOUND=1
+            fi
+        done
+        if [ -z \"$FOUND\" ]; then
+            if [ \"$TEST\" = \"/\" ]; then
+                TEST=\"\"
+            else
+                TEST=\"${TEST%/*}\"
+                if [ -z \"$TEST\" ]; then
+                    TEST=\"/\"
+                fi
+            fi
+        fi
+    done
+fi
+echo \\)
+"
+  "Script to find several dominating files on a remote host"
+)
+
+(defun tramp-hlo-dir-locals--all-files (orig-fun directory)
+  "Tramp version of dir-locals--all-files"
+  (let ((connection (file-remote-p directory)))
+    (if connection
+        (let* ((localdir (directory-file-name (file-local-name directory)))    
+               (file-1 dir-locals-file)
+               (file-2 (when (string-match "\\.el\\'" file-1)
+                         (replace-match "-2.el" t nil file-1)))
+               (vec (tramp-dissect-file-name directory))
+               )
+          (tramp-maybe-send-script vec tramp-hlo-dir-locals--all-files-script 
"dir_locals__all_files")
+          (mapcar (lambda (name) (concat connection name))
+                  (tramp-send-command-and-read vec
+                   (format "dir_locals__all_files %s %s %s"
+                           localdir file-1 file-2)
+                   )
+                  )
+          )
+      (funcall orig-fun directory)
+      )
+    )
+  )
+
+(defun tramp-hlo-locate-dominating-file-pred (connection vec file pred)
+  "Implementation of tramp-hlo-locate-dominating-file for a name predicate"
+  (tramp-maybe-send-script vec tramp-hlo-list-parents-script "list_parents")
+  (let* ((command (format "list_parents %s" (nth 6 vec)))
+         (parents (tramp-send-command-and-read vec command))
+         )
+    (while (and parents (not (funcall pred (concat connection (car parents)))))
+      (pop parents)
+      )
+    (if parents
+        (concat connection (car parents))
+      nil)
+    )
+  )
+
+(defun tramp-hlo-locate-dominating-file-list (connection vec file names)
+  "Implementation of tramp-hlo-locate-dominating-file for a list of names"
+  (tramp-maybe-send-script vec tramp-hlo-locate-dominating-file-multi-script 
"locate_dominating_file_multi")
+  (let* ((localfile (nth 6 vec))
+         (quoted-names (mapcar (lambda (name) (format "\"%s\"" name)) names))
+         (quoted-names-str (string-join names " "))
+         (command (format "locate_dominating_file_multi %s %s" localfile 
quoted-names-str))
+         (local-dominating (tramp-send-command-and-read vec command)))
+    (mapcar (lambda (result) (concat connection result)) local-dominating)
+    )
+  )
+
+(defun tramp-hlo-locate-dominating-file (orig-fun file name)
+  "Tramp version of locate-dominating-file"
+  (let ((connection (file-remote-p file)))
+    (if connection
+        (let ((vec (tramp-dissect-file-name file)))
+          (if (functionp name)
+              (tramp-hlo-locate-dominating-file-pred connection vec file name)
+            (let* ((names (if (listp name) name (list name)))
+                   (file-list (tramp-hlo-locate-dominating-file-list 
connection vec file names)))
+              (if file-list
+                  (file-name-directory (car file-list))
+                nil
+                )
+              )
+            )
+          )
+      (funcall orig-fun file name)
+      )
+    )
+  )
+
+(defun tramp-hlo-find-dominating-files (file names)
+  "Tramp specific function to find multiple dominating files"
+  (let ((connection (file-remote-p file)))
+    (if connection
+        (let ((vec (tramp-dissect-file-name file)))
+          (tramp-hlo-locate-dominating-file-list connection vec file names)
+          )
+      )
+    )
+  )
+
+(defun tramp-hlo-dir-locals-find-file (orig-fun file)
+  "Tramp implementation of dir-locals-find-file"
+  (let ((connection (file-remote-p file)))
+    (if connection
+        (let* ((locals-files (tramp-hlo-find-dominating-files 
(file-name-directory file)
+                                                   '(".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))
+      (funcall orig-fun file)
+      )
+    )
+  )
+
+(defun configure-tramp-hlo ()
+  (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)
+  )
+
+(provide 'tramp-hlo)

Reply via email to