summaryrefslogtreecommitdiff
path: root/lisp
diff options
context:
space:
mode:
authorJim Porter <jporterbugs@gmail.com>2022-10-11 22:11:04 -0700
committerJim Porter <jporterbugs@gmail.com>2022-10-17 18:48:52 -0700
commit3cc356abfef8294abcb91dc421e3c63a561a11b4 (patch)
tree79fd49b558b5880a04c41461794d380b682d93f1 /lisp
parent1beb389e472ab8132b478c9f24dd0ab6b7398670 (diff)
downloademacs-3cc356abfef8294abcb91dc421e3c63a561a11b4.tar.gz
emacs-3cc356abfef8294abcb91dc421e3c63a561a11b4.tar.bz2
emacs-3cc356abfef8294abcb91dc421e3c63a561a11b4.zip
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'.
Diffstat (limited to 'lisp')
-rw-r--r--lisp/files-x.el103
1 files changed, 98 insertions, 5 deletions
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'.
@@ -746,15 +784,27 @@ Execute BODY, and unwind connection-local variables."
`(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.
@@ -767,6 +817,49 @@ Call BODY-FUN with no args, and then unwind connection-local variables."
(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'."
(with-connection-local-variables path-separator))