aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2024-06-04 11:00:32 -0400
committerStefan Monnier2024-06-06 19:25:41 -0400
commit71ac0af5d0191cdf8ebc56066c7ca58dd93487cf (patch)
tree2a308c7e5b2c44212fe6cf025935235f23f0bb92
parentd12c9bc2a4c8383abdf7fa32b67f1ca0379227e3 (diff)
downloademacs-scratch/editorconfig.tar.gz
emacs-scratch/editorconfig.zip
(hack-dir-local-get-variables-functions): New hookscratch/editorconfig
Make it possible to provide more dir-local variables, such as done by the Editorconfig package. * lisp/files.el (hack-dir-local--get-variables): Make arg optional. (hack-dir-local-get-variables-functions): New hook. (hack-dir-local-variables): Run it instead of calling `hack-dir-local--get-variables`. * doc/lispref/variables.texi (Directory Local Variables): Document the new hook.
-rw-r--r--doc/lispref/variables.texi29
-rw-r--r--etc/NEWS4
-rw-r--r--lisp/files.el76
3 files changed, 93 insertions, 16 deletions
diff --git a/doc/lispref/variables.texi b/doc/lispref/variables.texi
index e05d3bb0f81..c63057e31ea 100644
--- a/doc/lispref/variables.texi
+++ b/doc/lispref/variables.texi
@@ -2277,6 +2277,35 @@ modification times of the associated directory local variables file
2277updates this list. 2277updates this list.
2278@end defvar 2278@end defvar
2279 2279
2280@defvar hack-dir-local-get-variables-functions
2281This special hook holds the functions that gather the directory-local
2282variables to use for a given buffer. By default it contains just the
2283function that obeys the other settings described in the present section.
2284But it can be used to add support for more sources of directory-local
2285variables, such as those used by other text editors.
2286
2287The functions on this hook are called with no argument, in the buffer to
2288which we intend to apply the directory-local variables, after the
2289buffer's major mode function has been run, so it can use sources of
2290information such as @code{major-mode} or @code{buffer-file-name} to find
2291the variables that should be applied.
2292
2293It should return either a cons cell of the form @code{(@var{directory}
2294. @var{alist})} or a list of such cons-cells. A @code{nil} return value
2295means that it found no directory-local variables. @var{directory}
2296should be a string: the name of the directory to which the variables
2297apply. @var{alist} is a list of variables together with their values
2298that apply to the current buffer, where every element is of the form
2299@code{(@var{varname} . @var{value})}.
2300
2301The various @var{alist} returned by these functions will be combined,
2302and in case of conflicts, the settings coming from deeper directories
2303will take precedence over those coming from higher directories in the
2304directory hierarchy. Finally, since this hook is run every time we visit
2305a file it is important to try and keep those functions efficient, which
2306will usually require some of caching.
2307@end defvar
2308
2280@defvar enable-dir-local-variables 2309@defvar enable-dir-local-variables
2281If @code{nil}, directory-local variables are ignored. This variable 2310If @code{nil}, directory-local variables are ignored. This variable
2282may be useful for modes that want to ignore directory-locals while 2311may be useful for modes that want to ignore directory-locals while
diff --git a/etc/NEWS b/etc/NEWS
index 52486b7bbe9..14e5dafbadc 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -2163,6 +2163,10 @@ completion candidate.
2163 2163
2164* Lisp Changes in Emacs 30.1 2164* Lisp Changes in Emacs 30.1
2165 2165
2166** New hook 'hack-dir-local-get-variables-functions'.
2167This can be used to provide support for other directory-local settings
2168beside '.dir-locals.el'.
2169
2166+++ 2170+++
2167** 'auto-coding-functions' can know the name of the file. 2171** 'auto-coding-functions' can know the name of the file.
2168The functions on this hook take can now find the name of the file to 2172The functions on this hook take can now find the name of the file to
diff --git a/lisp/files.el b/lisp/files.el
index 210cd0fa7ad..a36ac6b1318 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -3494,6 +3494,8 @@ we don't actually set it to the same mode the buffer already has."
3494 ;; Check for auto-mode-alist entry in dir-locals. 3494 ;; Check for auto-mode-alist entry in dir-locals.
3495 (with-demoted-errors "Directory-local variables error: %s" 3495 (with-demoted-errors "Directory-local variables error: %s"
3496 ;; Note this is a no-op if enable-local-variables is nil. 3496 ;; Note this is a no-op if enable-local-variables is nil.
3497 ;; We don't use `hack-dir-local-get-variables-functions' here, because
3498 ;; modes are specific to Emacs.
3497 (let* ((mode-alist (cdr (hack-dir-local--get-variables 3499 (let* ((mode-alist (cdr (hack-dir-local--get-variables
3498 (lambda (key) (eq key 'auto-mode-alist)))))) 3500 (lambda (key) (eq key 'auto-mode-alist))))))
3499 (set-auto-mode--apply-alist mode-alist keep-mode-if-same t))) 3501 (set-auto-mode--apply-alist mode-alist keep-mode-if-same t)))
@@ -4769,7 +4771,7 @@ Return the new class name, which is a symbol named DIR."
4769 4771
4770(defvar hack-dir-local-variables--warned-coding nil) 4772(defvar hack-dir-local-variables--warned-coding nil)
4771 4773
4772(defun hack-dir-local--get-variables (predicate) 4774(defun hack-dir-local--get-variables (&optional predicate)
4773 "Read per-directory local variables for the current buffer. 4775 "Read per-directory local variables for the current buffer.
4774Return a cons of the form (DIR . ALIST), where DIR is the 4776Return a cons of the form (DIR . ALIST), where DIR is the
4775directory name (maybe nil) and ALIST is an alist of all variables 4777directory name (maybe nil) and ALIST is an alist of all variables
@@ -4799,6 +4801,16 @@ PREDICATE is passed to `dir-locals-collect-variables'."
4799 (dir-locals-get-class-variables class) 4801 (dir-locals-get-class-variables class)
4800 dir-name nil predicate)))))) 4802 dir-name nil predicate))))))
4801 4803
4804(defvar hack-dir-local-get-variables-functions
4805 (list #'hack-dir-local--get-variables)
4806 "Special hook to compute the set of dir-local variables.
4807Every function is called without arguments and should return either
4808a cons of the form (DIR . ALIST) or a (possibly empty) list of such conses,
4809where ALIST is an alist of (VAR . VAL) settings.
4810DIR should be a string (a directory name) and is used to obey
4811`safe-local-variable-directories'.
4812This hook is run after the major mode has been setup.")
4813
4802(defun hack-dir-local-variables () 4814(defun hack-dir-local-variables ()
4803 "Read per-directory local variables for the current buffer. 4815 "Read per-directory local variables for the current buffer.
4804Store the directory-local variables in `dir-local-variables-alist' 4816Store the directory-local variables in `dir-local-variables-alist'
@@ -4806,21 +4818,53 @@ and `file-local-variables-alist', without applying them.
4806 4818
4807This does nothing if either `enable-local-variables' or 4819This does nothing if either `enable-local-variables' or
4808`enable-dir-local-variables' are nil." 4820`enable-dir-local-variables' are nil."
4809 (let* ((items (hack-dir-local--get-variables nil)) 4821 (let (items)
4810 (dir-name (car items)) 4822 (when (and enable-local-variables
4811 (variables (cdr items))) 4823 enable-dir-local-variables
4812 (when variables 4824 (or enable-remote-dir-locals
4813 (dolist (elt variables) 4825 (not (file-remote-p (or (buffer-file-name)
4814 (if (eq (car elt) 'coding) 4826 default-directory)))))
4815 (unless hack-dir-local-variables--warned-coding 4827 (run-hook-wrapped 'hack-dir-local-get-variables-functions
4816 (setq hack-dir-local-variables--warned-coding t) 4828 (lambda (fun)
4817 (display-warning 'files 4829 (let ((res (funcall fun)))
4818 "Coding cannot be specified by dir-locals")) 4830 (cond
4819 (unless (memq (car elt) '(eval mode)) 4831 ((null res))
4820 (setq dir-local-variables-alist 4832 ((consp (car-safe res))
4821 (assq-delete-all (car elt) dir-local-variables-alist))) 4833 (setq items (append res items)))
4822 (push elt dir-local-variables-alist))) 4834 (t (push res items)))))))
4823 (hack-local-variables-filter variables dir-name)))) 4835 ;; Sort the entries from nearest dir to furthest dir.
4836 (setq items (sort (nreverse items)
4837 :key (lambda (x) (length (car-safe x))) :reverse t))
4838 ;; Filter out duplicates, preferring the settings from the nearest dir
4839 ;; and from the first hook function.
4840 (let ((seen nil))
4841 (dolist (item items)
4842 (when seen ;; Special case seen=nil since it's the most common case.
4843 (setcdr item (seq-filter (lambda (vv) (not (memq (car-safe vv) seen)))
4844 (cdr item))))
4845 (setq seen (nconc (seq-difference (mapcar #'car (cdr item))
4846 '(eval mode))
4847 seen))))
4848 ;; Rather than a loop, maybe we should handle all the dirs
4849 ;; "together", e.g. prompting the user only once. But if so, we'd
4850 ;; probably want to also merge the prompt for file-local vars,
4851 ;; which comes from the call to `hack-local-variables-filter' in
4852 ;; `hack-local-variables'.
4853 (dolist (item items)
4854 (let ((dir-name (car items))
4855 (variables (cdr items)))
4856 (when variables
4857 (dolist (elt variables)
4858 (if (eq (car elt) 'coding)
4859 (unless hack-dir-local-variables--warned-coding
4860 (setq hack-dir-local-variables--warned-coding t)
4861 (display-warning 'files
4862 "Coding cannot be specified by dir-locals"))
4863 (unless (memq (car elt) '(eval mode))
4864 (setq dir-local-variables-alist
4865 (assq-delete-all (car elt) dir-local-variables-alist)))
4866 (push elt dir-local-variables-alist)))
4867 (hack-local-variables-filter variables dir-name))))))
4824 4868
4825(defun hack-dir-local-variables-non-file-buffer () 4869(defun hack-dir-local-variables-non-file-buffer ()
4826 "Apply directory-local variables to a non-file buffer. 4870 "Apply directory-local variables to a non-file buffer.