all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Neil Roberts <bpeeluk@yahoo.co.uk>
To: 35522@debbugs.gnu.org
Subject: bug#35522: [PATCH v2] Let dir locals for more specific modes override those from less
Date: Wed,  1 May 2019 13:49:13 +0200	[thread overview]
Message-ID: <20190501114913.513-1-bpeeluk@yahoo.co.uk> (raw)
In-Reply-To: <20181115180117.10423-1-bpeeluk@yahoo.co.uk>

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.
---
 doc/emacs/custom.texi | 22 ++++++++++++++++++++
 lisp/files.el         | 47 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 69 insertions(+)

diff --git a/doc/emacs/custom.texi b/doc/emacs/custom.texi
index 22e352ef9f..3a85907b45 100644
--- a/doc/emacs/custom.texi
+++ b/doc/emacs/custom.texi
@@ -1377,6 +1377,28 @@ Directory Variables
 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 c05d70a00e..e104e49472 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -4108,6 +4108,52 @@ dir-locals-find-file
 (declare-function map-merge-with "map" (type function &rest maps))
 (declare-function map-merge "map" (type &rest maps))
 
+(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
@@ -4145,6 +4191,7 @@ dir-locals-read-from-dir
                                     variables
                                     newvars))))))
       (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))
-- 
2.17.2






  parent reply	other threads:[~2019-05-01 11:49 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-11-15 13:21 bug#33400: 26.1; Order changed for overriding “nil” mode in dir-locals Neil Roberts
2018-11-15 18:01 ` bug#33400: [PATCH] Let dir locals for more specific modes override those from less Neil Roberts
2019-04-27 18:05   ` Noam Postavsky
2019-04-29  7:45     ` Neil Roberts
2019-04-29 14:41       ` Eli Zaretskii
2019-04-29 12:58     ` Michael Albinus
2019-05-08  7:18     ` Neil Roberts
2019-05-10  1:30       ` Noam Postavsky
2019-05-10  6:44         ` Eli Zaretskii
2019-05-10 10:57           ` bug#33400: [PATCH v3] " Neil Roberts
2019-05-12 14:03             ` Noam Postavsky
2019-05-01 11:49   ` Neil Roberts [this message]
2018-11-16  9:48 ` bug#33400: 26.1; Order changed for overriding “nil” mode in dir-locals Phil Sainty
2019-04-25 17:08 ` bug#33400: BUMP: please merge the fix for this bug Mark Janes
2019-04-26 17:46 ` bug#33400: Merge with bug#30008? Kévin Le Gouguec
2019-04-27 18:06   ` Noam Postavsky

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190501114913.513-1-bpeeluk@yahoo.co.uk \
    --to=bpeeluk@yahoo.co.uk \
    --cc=35522@debbugs.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.