Commit 02bee786 authored by Neil Roberts's avatar Neil Roberts Committed by Noam Postavsky

Let dir locals for more specific modes override those from less

The list of dir local variables to apply is now sorted by the number
of parent modes of the mode used as the key in the association list.
That way when the variables are applied in order the variables from
more specific modes will override those from less specific modes.

If there are directory entries in the list then they are sorted in
order of name length.  The list of modes for that dir is then
recursively sorted with the same mechanism.  That way variables tied
to a particular subdirectory override those in in a parent directory.

Previously the behaviour didn’t seem to be well defined anyway and was
dependent on the order they appeared in the file.  However this order
was changed in version 26.1 and it probably also depended on the
number of dir-local files that are merged.

Bug#33400

* lisp/files.el (dir-locals-get-sort-score, dir-locals-sort-variables)
(dir-locals-read-from-dir): Sort the dir locals so that more precise
modes and directory-specific entries have override lesser ones.
* doc/emacs/custom.texi (Directory Variables): Document the priority.
parent b1235f9a
Pipeline #1636 passed with stage
in 26 minutes and 40 seconds
......@@ -1375,6 +1375,28 @@ be applied in the current directory, not in any subdirectories.
Finally, it specifies a different @file{ChangeLog} file name for any
file in the @file{src/imported} subdirectory.
If the @file{.dir-locals.el} file contains multiple different values
for a variable using different mode names or directories, the values
will be applied in an order such that the values for more specific
modes take priority over more generic modes. Values specified under a
directory have even more priority. For example:
@example
((nil . ((fill-column . 40)))
(c-mode . ((fill-column . 50)))
(prog-mode . ((fill-column . 60)))
("narrow-files" . ((nil . ((fill-column . 20))))))
@end example
Files that use @code{c-mode} also match @code{prog-mode} because the
former inherits from the latter. The value used for
@code{fill-column} in C files will however be @code{50} because the
mode name is more specific than @code{prog-mode}. Files using other
modes inheriting from @code{prog-mode} will use @code{60}. Any file
under the directory @file{narrow-files} will use the value @code{20}
even if they use @code{c-mode} because directory entries have priority
over mode entries.
You can specify the variables @code{mode}, @code{eval}, and
@code{unibyte} in your @file{.dir-locals.el}, and they have the same
meanings as they would have in file local variables. @code{coding}
......
......@@ -4026,6 +4026,52 @@ This function returns either:
;; No cache entry.
locals-dir)))
(defun dir-locals--get-sort-score (node)
"Return a number used for sorting the definitions of dir locals.
NODE is assumed to be a cons cell where the car is either a
string or a symbol representing a mode name.
If it is a mode then the the depth of the mode (ie, how many
parents that mode has) will be returned.
If it is a string then the length of the string plus 1000 will be
returned.
Otherwise it returns -1.
That way the value can be used to sort the list such that deeper
modes will be after the other modes. This will be followed by
directory entries in order of length. If the entries are all
applied in order then that means the more specific modes will
override the values specified by the earlier modes and directory
variables will override modes."
(let ((key (car node)))
(cond ((null key) -1)
((symbolp key)
(let ((mode key)
(depth 0))
(while (setq mode (get mode 'derived-mode-parent))
(setq depth (1+ depth)))
depth))
((stringp key)
(+ 1000 (length key)))
(t -2))))
(defun dir-locals--sort-variables (variables)
"Sorts VARIABLES so that applying them in order has the right effect.
The variables are compared by dir-locals--get-sort-score.
Directory entries are then recursively sorted using the same
criteria."
(setq variables (sort variables
(lambda (a b)
(< (dir-locals--get-sort-score a)
(dir-locals--get-sort-score b)))))
(dolist (n variables)
(when (stringp (car n))
(setcdr n (dir-locals--sort-variables (cdr n)))))
variables)
(defun dir-locals-read-from-dir (dir)
"Load all variables files in DIR and register a new class and instance.
DIR is the absolute name of a directory which must contain at
......@@ -4054,6 +4100,7 @@ Return the new class name, which is a symbol named DIR."
(read (current-buffer))))
(end-of-file nil))))
(setq success latest))
(setq variables (dir-locals--sort-variables variables))
(dir-locals-set-class-variables class-name variables)
(dir-locals-set-directory-class dir class-name success)
class-name))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment