diff --git a/doc/emacs/custom.texi b/doc/emacs/custom.texi index b07362f3cea..3fd655048b4 100644 --- a/doc/emacs/custom.texi +++ b/doc/emacs/custom.texi @@ -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} diff --git a/lisp/files.el b/lisp/files.el index d7ed2487862..f3b502095dd 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -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))