From 077a6a8b83cd1fb6efa9f169831ad6adec52312a Mon Sep 17 00:00:00 2001 From: Mauro Aranda Date: Sat, 21 Oct 2023 11:02:36 -0300 Subject: [PATCH] Allow specifying the dir locals file to edit (Bug#66663) * lisp/files-x.el (modify-dir-local-variable): Take a 5th optional argument, the filename of the dir locals file to modify. (add-dir-local-variable, delete-dir-local-variable) (copy-file-locals-to-dir-locals): Optionally read the filename to modify, and pass it to modify-dir-local-variable. * etc/NEWS: Announce the change. * doc/emacs/custom.texi (Directory Variables): Document the new functionality. --- doc/emacs/custom.texi | 8 ++- etc/NEWS | 10 ++++ lisp/files-x.el | 110 ++++++++++++++++++++++++++++++------------ 3 files changed, 95 insertions(+), 33 deletions(-) diff --git a/doc/emacs/custom.texi b/doc/emacs/custom.texi index 8c30f26bbf7..adecc873163 100644 --- a/doc/emacs/custom.texi +++ b/doc/emacs/custom.texi @@ -1507,7 +1507,13 @@ Directory Variables entry defining the directory-local variable. @kbd{M-x delete-dir-local-variable} deletes an entry. @kbd{M-x copy-file-locals-to-dir-locals} copies the file-local variables in the -current file into @file{.dir-locals.el}. +current file into @file{.dir-locals.el}, or @file{.dir-locals-2.el} if +that file is also present. + +With a prefix argument, all three commands prompt for the file you +want to modify. Although it doesn't have to exist, you must enter a +valid filename, either @file{.dir-locals.el} or +@file{.dir-locals-2.el}. @findex dir-locals-set-class-variables @findex dir-locals-set-directory-class diff --git a/etc/NEWS b/etc/NEWS index 3d4cdd876b3..d48466be305 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -187,6 +187,11 @@ right-aligned to is controlled by the new user option It can be used to add, remove and reorder functions that change the appearance of every tab on the tab bar. ++++ +** New optional argument for modifying directory local variables +The commands 'add-dir-local-variable', 'delete-dir-local-variable' and +'copy-file-locals-to-dir-locals' now take an optional prefix argument, +to enter the file you want to modify. ** Miscellaneous --- @@ -1337,6 +1342,11 @@ Since circular alias chains now cannot occur, 'function-alias-p', 'indirect-function' and 'indirect-variable' will never signal an error. Their 'noerror' arguments have no effect and are therefore obsolete. +--- +** New optional argument to 'modify-dir-local-variable' +A 5th argument, optional, has been added to +'modify-dir-local-variable'. It can be used to specify which +dir-locals file to modify. * Changes in Emacs 30.1 on Non-Free Operating Systems diff --git a/lisp/files-x.el b/lisp/files-x.el index 3ba7632d253..ef3adcb9cd3 100644 --- a/lisp/files-x.el +++ b/lisp/files-x.el @@ -31,6 +31,7 @@ ;;; Code: (eval-when-compile (require 'subr-x)) ; for string-trim-right +(declare-function dosified-file-name "dos-fns" (file-name)) ;;; Commands to add/delete file-local/directory-local variables. @@ -410,7 +411,7 @@ delete-file-local-variable-prop-line (defvar auto-insert) ; from autoinsert.el -(defun modify-dir-local-variable (mode variable value op) +(defun modify-dir-local-variable (mode variable value op &optional file) "Modify directory-local VARIABLE in .dir-locals.el depending on operation OP. If OP is `add-or-replace' then delete all existing settings of @@ -422,28 +423,37 @@ modify-dir-local-variable this file in the current directory. If OP is `delete' then delete all existing settings of VARIABLE -from the MODE alist ignoring the input argument VALUE." +from the MODE alist ignoring the input argument VALUE. + +Optional argument FILE, when non-nil, specifies what file to modify. It +should be an expanded filename." (catch 'exit (unless enable-local-variables (throw 'exit (message "Directory-local variables are disabled"))) - (let* ((dir-or-cache (and (buffer-file-name) - (not (file-remote-p (buffer-file-name))) - (dir-locals-find-file (buffer-file-name)))) - (variables-file - ;; If there are several .dir-locals, the user probably - ;; wants to edit the last one (the highest priority). - (cond ((stringp dir-or-cache) - (car (last (dir-locals--all-files dir-or-cache)))) - ((consp dir-or-cache) ; result from cache - ;; If cache element has an mtime, assume it came - ;; from a file. Otherwise, assume it was set - ;; directly. - (if (nth 2 dir-or-cache) - (car (last (dir-locals--all-files (car dir-or-cache)))) - (cadr dir-or-cache))) - ;; Try to make a proper file-name. - (t (expand-file-name dir-locals-file)))) - variables) + (let ((variables-file + (if (stringp file) + file + (let ((dir-or-cache + (and (buffer-file-name) + (not (file-remote-p (buffer-file-name))) + (dir-locals-find-file (buffer-file-name))))) + ;; If there are several .dir-locals, the user probably + ;; wants to edit the last one (the highest priority). + (cond + ((stringp dir-or-cache) + (car (last (dir-locals--all-files dir-or-cache)))) + ((consp dir-or-cache) ; result from cache + ;; If cache element has an mtime, assume it came + ;; from a file. Otherwise, assume it was set + ;; directly. + (if (nth 2 dir-or-cache) + (car (last (dir-locals--all-files (car dir-or-cache)))) + (cadr dir-or-cache))) + ;; Try to make a proper file-name. + (t (expand-file-name (if (eq system-type 'ms-dos) + (dosified-file-name dir-locals-file) + dir-locals-file))))))) + variables) ;; I can't be bothered to handle this case right now. ;; Dir locals were set directly from a class. You need to ;; directly modify the class in dir-locals-class-alist. @@ -527,33 +537,69 @@ dir-locals-to-string (cdr mode-variables) "\n")))) variables "\n"))) +(defun read-dir-locals-file () + "Read a dir-locals filename using completion. +Intended to be used in the `interactive' spec of `add-dir-local-variable', +`delete-dir-local-variable' and `copy-file-locals-to-dir-locals'. + +Returns the filename, expanded." + (expand-file-name + (read-file-name "File" nil nil + (lambda (fname) + (member (file-name-nondirectory fname) + (list dir-locals-file + (replace-regexp-in-string + ".el$" "-2.el" dir-locals-file)))) + dir-locals-file))) + ;;;###autoload -(defun add-dir-local-variable (mode variable value) - "Add directory-local VARIABLE with its VALUE and MODE to .dir-locals.el." +(defun add-dir-local-variable (mode variable value &optional file) + "Add directory-local VARIABLE with its VALUE and MODE to .dir-locals.el. + +With a prefix argument, prompt for the file to modify. + +When called from Lisp, FILE may be the expanded name of the dir-locals file +where to add VARIABLE." (interactive (let (variable) (list (read-file-local-variable-mode) (setq variable (read-file-local-variable "Add directory-local variable")) - (read-file-local-variable-value variable)))) - (modify-dir-local-variable mode variable value 'add-or-replace)) + (read-file-local-variable-value variable) + (when current-prefix-arg + (read-dir-locals-file))))) + (modify-dir-local-variable mode variable value 'add-or-replace file)) ;;;###autoload -(defun delete-dir-local-variable (mode variable) - "Delete all MODE settings of file-local VARIABLE from .dir-locals.el." +(defun delete-dir-local-variable (mode variable &optional file) + "Delete all MODE settings of dir-local VARIABLE from .dir-locals.el. + +With a prefix argument, prompt for the file to modify. + +When called from Lisp, FILE may be the expanded name of the dir-locals file +from where to delete VARIABLE." (interactive (list (read-file-local-variable-mode) - (read-file-local-variable "Delete directory-local variable"))) - (modify-dir-local-variable mode variable nil 'delete)) + (read-file-local-variable "Delete directory-local variable") + (when current-prefix-arg + (read-dir-locals-file)))) + (modify-dir-local-variable mode variable nil 'delete file)) ;;;###autoload -(defun copy-file-locals-to-dir-locals () - "Copy file-local variables to .dir-locals.el." - (interactive) +(defun copy-file-locals-to-dir-locals (&optional file) + "Copy file-local variables to .dir-locals.el. + +With a prefix argument, prompt for the file to modify. + +When called from Lisp, FILE may be the expanded name of the dir-locals file +where to copy the file-local variables." + (interactive + (list (when current-prefix-arg + (read-dir-locals-file)))) (dolist (elt file-local-variables-alist) (unless (assq (car elt) dir-local-variables-alist) - (add-dir-local-variable major-mode (car elt) (cdr elt))))) + (add-dir-local-variable major-mode (car elt) (cdr elt) file)))) ;;;###autoload (defun copy-dir-locals-to-file-locals () -- 2.34.1