From 3cc356abfef8294abcb91dc421e3c63a561a11b4 Mon Sep 17 00:00:00 2001 From: Jim Porter Date: Tue, 11 Oct 2022 22:11:04 -0700 Subject: Add helpers to dynamically assign connection-local values * lisp/files-x.el (connection-local-criteria) (connection-local-profile-name-for-setq): New variables. (with-connection-local-variables-1): ... let-bind them here. (connection-local-update-profile-variables) (connection-local-profile-name-for-criteria): New functions. (with-connection-local-application-variables, setq-connection-local): New macros. * test/lisp/files-x-tests.el: Require 'tramp-integration' (files-x-test--variable5, remote-lazy-var): New variables. (files-x-test-hack-connection-local-variables-apply): Expand checks. (files-x-test-with-connection-local-variables): Remove 'hack-connection-local-variables-apply' check (it belongs in the above test), and expand some other checks. (files-x-test--get-lazy-var, files-x-test--set-lazy-var): New functions. (files-x-test-connection-local-update-profile-variables) (files-x-test-setq-connection-local): New tests. * doc/lispref/variables.texi (Connection Local Variables): Split into two subsections and document the new features. * etc/NEWS: Announce 'setq-connection-local'. --- lisp/files-x.el | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 5 deletions(-) (limited to 'lisp/files-x.el') diff --git a/lisp/files-x.el b/lisp/files-x.el index 0131d495f27..3516592fc3e 100644 --- a/lisp/files-x.el +++ b/lisp/files-x.el @@ -620,6 +620,18 @@ PROFILES is a list of connection profiles (symbols)." :group 'tramp :version "29.1") +(defvar connection-local-criteria nil + "The current connection-local criteria, or nil. +This is set while executing the body of +`with-connection-local-variables'.") + +(defvar connection-local-profile-name-for-setq nil + "The current connection-local profile name, or nil. +This is the name of the profile to use when setting variables via +`setq-connection-local'. Its value is derived from +`connection-local-criteria' and is set while executing the body +of `with-connection-local-variables'.") + (defsubst connection-local-normalize-criteria (criteria) "Normalize plist CRITERIA according to properties. Return a reordered plist." @@ -696,6 +708,23 @@ in order." (customize-set-variable 'connection-local-profile-alist connection-local-profile-alist)) +;;;###autoload +(defun connection-local-update-profile-variables (profile variables) + "Update the variable settings for PROFILE in-place. +VARIABLES is a list that declares connection-local variables for +the connection profile. An element in VARIABLES is an alist +whose elements are of the form (VAR . VALUE). + +Unlike `connection-local-set-profile-variables' (which see), this +function preserves the values of any existing variable +definitions that aren't listed in VARIABLES." + (when-let ((existing-variables + (nreverse (connection-local-get-profile-variables profile)))) + (dolist (var variables) + (setf (alist-get (car var) existing-variables) (cdr var))) + (setq variables (nreverse existing-variables))) + (connection-local-set-profile-variables profile variables)) + (defun hack-connection-local-variables (criteria) "Read connection-local variables according to CRITERIA. Store the connection-local variables in buffer local @@ -738,6 +767,15 @@ If APPLICATION is nil, `connection-local-default-application' is used." :user ,(file-remote-p default-directory 'user) :machine ,(file-remote-p default-directory 'host)))) +(defun connection-local-profile-name-for-criteria (criteria) + "Get a connection-local profile name based on CRITERIA." + (when criteria + (let (print-level print-length) + (intern (concat + "autogenerated-connection-local-profile/" + (prin1-to-string + (connection-local-normalize-criteria criteria))))))) + ;;;###autoload (defmacro with-connection-local-variables (&rest body) "Apply connection-local variables according to `default-directory'. @@ -745,16 +783,28 @@ Execute BODY, and unwind connection-local variables." (declare (debug t)) `(with-connection-local-variables-1 (lambda () ,@body))) +;;;###autoload +(defmacro with-connection-local-application-variables (application &rest body) + "Apply connection-local variables for APPLICATION in `default-directory'. +Execute BODY, and unwind connection-local variables." + (declare (debug t) (indent 1)) + `(let ((connection-local-default-application ,application)) + (with-connection-local-variables-1 (lambda () ,@body)))) + ;;;###autoload (defun with-connection-local-variables-1 (body-fun) "Apply connection-local variables according to `default-directory'. Call BODY-FUN with no args, and then unwind connection-local variables." (if (file-remote-p default-directory) - (let ((enable-connection-local-variables t) - (old-buffer-local-variables (buffer-local-variables)) - connection-local-variables-alist) - (hack-connection-local-variables-apply - (connection-local-criteria-for-default-directory)) + (let* ((enable-connection-local-variables t) + (connection-local-criteria + (connection-local-criteria-for-default-directory)) + (connection-local-profile-name-for-setq + (connection-local-profile-name-for-criteria + connection-local-criteria)) + (old-buffer-local-variables (buffer-local-variables)) + connection-local-variables-alist) + (hack-connection-local-variables-apply connection-local-criteria) (unwind-protect (funcall body-fun) ;; Cleanup. @@ -766,6 +816,49 @@ Call BODY-FUN with no args, and then unwind connection-local variables." ;; No connection-local variables to apply. (funcall body-fun))) +;;;###autoload +(defmacro setq-connection-local (&rest pairs) + "Set each VARIABLE connection-locally to VALUE. + +When `connection-local-profile-name-for-setq' is set, assign each +variable's value on that connection profile, and set that profile +for `connection-local-criteria'. You can use this in combination +with `with-connection-local-variables', as in + + (with-connection-local-variables + (setq-connection-local VARIABLE VALUE)) + +If there's no connection-local profile to use, just set the +variables normally, as with `setq'. + +The variables are literal symbols and should not be quoted. The +second VALUE is not computed until after the first VARIABLE is +set, and so on; each VALUE can use the new value of variables set +earlier in the `setq-connection-local'. The return value of the +`setq-connection-local' form is the value of the last VALUE. + +\(fn [VARIABLE VALUE]...)" + (declare (debug setq)) + (unless (zerop (mod (length pairs) 2)) + (error "PAIRS must have an even number of variable/value members")) + (let ((set-expr nil) + (profile-vars nil)) + (while pairs + (unless (symbolp (car pairs)) + (error "Attempting to set a non-symbol: %s" (car pairs))) + (push `(set ',(car pairs) ,(cadr pairs)) set-expr) + (push `(cons ',(car pairs) ,(car pairs)) profile-vars) + (setq pairs (cddr pairs))) + `(prog1 + ,(macroexp-progn (nreverse set-expr)) + (when connection-local-profile-name-for-setq + (connection-local-update-profile-variables + connection-local-profile-name-for-setq + (list ,@(nreverse profile-vars))) + (connection-local-set-profiles + connection-local-criteria + connection-local-profile-name-for-setq))))) + ;;;###autoload (defun path-separator () "The connection-local value of `path-separator'." -- cgit v1.2.3