GNU bug report logs - #18721
25.0.50; choose major mode from .dir-locals.el

Previous Next

Package: emacs;

Reported by: Tom Tromey <tom <at> tromey.com>

Date: Tue, 14 Oct 2014 20:08:01 UTC

Severity: wishlist

Tags: patch

Merged with 18188

Found in version 25.0.50

Fixed in version 28.1

Done: Lars Ingebrigtsen <larsi <at> gnus.org>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 18721 in the body.
You can then email your comments to 18721 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#18721; Package emacs. (Tue, 14 Oct 2014 20:08:01 GMT) Full text and rfc822 format available.

Acknowledgement sent to Tom Tromey <tom <at> tromey.com>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Tue, 14 Oct 2014 20:08:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Tom Tromey <tom <at> tromey.com>
To: bug-gnu-emacs <at> gnu.org
Subject: 25.0.50; choose major mode from .dir-locals.el
Date: Tue, 14 Oct 2014 14:06:39 -0600
It would be nice if there were a way to associate
file extensions with major modes in .dir-locals.el.
This would let projects use project-specific extensions
without requiring per-file mode cookies or having
everybody modify their .emacs




In GNU Emacs 25.0.50.3 (x86_64-unknown-linux-gnu, GTK+ Version 3.10.9)
 of 2014-10-09 on pokyo
Repository revision: 118080 eggert <at> cs.ucla.edu-20141009065410-dhgjku4gclzl71kj
Windowing system distributor `Fedora Project', version 11.0.11404000
System Description:	Fedora release 20 (Heisenbug)

Configured using:
 `configure --prefix=/home/tromey/Emacs/install'

Configured features:
XPM JPEG TIFF GIF PNG SOUND GPM DBUS GSETTINGS NOTIFY ACL LIBSELINUX
GNUTLS LIBXML2 FREETYPE LIBOTF XFT ZLIB

Important settings:
  value of $LANG: en_US.UTF-8
  locale-coding-system: utf-8-unix

Major mode: C++/l

Minor modes in effect:
  shell-dirtrack-mode: t
  erc-spelling-mode: t
  erc-truncate-mode: t
  diff-auto-refine-mode: t
  bug-reference-prog-mode: t
  flyspell-mode: t
  which-function-mode: t
  projectile-global-mode: t
  projectile-mode: t
  global-auto-revert-mode: t
  desktop-save-mode: t
  erc-status-mode: t
  erc-services-mode: t
  erc-list-mode: t
  erc-menu-mode: t
  erc-autojoin-mode: t
  erc-ring-mode: t
  erc-networks-mode: t
  erc-pcomplete-mode: t
  erc-track-mode: t
  erc-track-minor-mode: t
  erc-match-mode: t
  erc-button-mode: t
  erc-fill-mode: t
  erc-stamp-mode: t
  erc-netsplit-mode: t
  erc-irccontrols-mode: t
  erc-noncommands-mode: t
  erc-move-to-prompt-mode: t
  erc-readonly-mode: t
  savehist-mode: t
  tooltip-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t
  column-number-mode: t
  line-number-mode: t
  auto-fill-function: tromey-c++-do-auto-fill
  abbrev-mode: t

Recent input:
n e n o SPC 1 3 RET SPC 3 0 : SPC SPC SPC 1 3 SPC SPC 
SPC SPC 9 5 SPC [ SPC SPC SPC 0 ] SPC c o l s p a n 
SPC 1 RET SPC 3 2 : SPC SPC SPC 1 3 SPC SPC SPC 1 0 
8 SPC [ SPC SPC 1 3 ] SPC x d e l t a SPC SPC RET SPC 
3 3 : SPC SPC SPC 1 3 SPC SPC SPC 1 0 8 SPC [ SPC SPC 
SPC 0 ] SPC c o l s p a n SPC 8 RET RET E x c e p t 
i o n SPC t a b l e : RET k i n d SPC SPC SPC SPC SPC 
SPC s t a c k SPC SPC SPC SPC s t a r t SPC SPC SPC 
SPC SPC SPC e n d RET SPC c a t c h SPC SPC SPC SPC 
SPC SPC SPC SPC 0 SPC SPC SPC SPC SPC SPC SPC 1 4 SPC 
SPC SPC SPC SPC SPC SPC 4 3 RET SPC f i n a l l y SPC 
SPC SPC SPC SPC SPC 0 SPC SPC SPC SPC SPC SPC SPC 1 
4 SPC SPC SPC SPC SPC SPC SPC 7 9 ESC [ 2 0 1 ~ RET 
C-a C-k C-x C-x C-g C-u C-@ C-x C-s <switch-frame> 
C-z n C-z n <f10> <f10> C-z n C-z n <f10> <f10> <switch-frame> 
<switch-frame> C-z n C-z n <f10> <f10> <f10> <switch-frame> 
<switch-frame> C-x C-c <switch-frame> C-z n M-x r e 
o p r <M-backspace> r e p r <backspace> <backspace> 
p o e r <backspace> r <backspace> t <backspace> <backspace> 
r t - e m <tab> <return>

Recent messages:
C-z C-z is undefined
Quit
(New file)
When done with a buffer, type C-x #
Mark set
Auto-saving...
Quit
Saving file /tmp/L...
Wrote /tmp/L
(No files need saving)

Load-path shadows:
/home/tromey/.emacs.d/elpa/css-mode-1.0/css-mode hides /home/tromey/Emacs/install/share/emacs/25.0.50/lisp/textmodes/css-mode
/home/tromey/.emacs.d/elpa/bubbles-0.5/bubbles hides /home/tromey/Emacs/install/share/emacs/25.0.50/lisp/play/bubbles
/home/tromey/Emacs/install/share/emacs/25.0.50/lisp/loaddefs hides /home/tromey/Emacs/install/share/emacs/25.0.50/lisp/obsolete/loaddefs

Features:
(shadow emacsbug nxml-uchnm rng-xsd xsd-regexp rng-cmpct rng-nxml
rng-valid rng-loc rng-uri rng-parse nxml-parse rng-match rng-dt rng-util
rng-pttrn nxml-ns nxml-mode nxml-outln nxml-rap nxml-util nxml-glyph
nxml-enc xmltok vc-annotate ido log-edit eww log-view pcvs-util novice
etags find-dired ffap two-column autoconf autoconf-mode sh-script smie
executable shell find-file eieio-opt speedbar sb-image ezimage dframe
help-mode gdb-shell webjump dabbrev copyright idutils derived gnus-fun
flow-fill gnus-html xml mm-url mailalias bbdb-sc supercite regi
mail-hist nnir shr-color color xterm url-http url-gw url-auth url-queue
url-cache url url-proxy url-privacy url-expand url-methods url-history
url-cookie url-domsuf url-util url-parse url-vars shr browse-url
misearch multi-isearch erc-spelling erc-truncate bbdb-gui bbdb-hooks
mule-util sort smiley gnus-cite mm-archive gnus-async gnus-bcklg qp
gnus-ml disp-table gnus-topic nndraft nnmh nnfolder utf-7 bbdb-gnus
bbdb-snarf mail-extr bbdb-com warnings cl gv gnutls network-stream
starttls gnus-agent gnus-srvr gnus-score score-mode nnvirtual gnus-msg
nntp gnus-cache gnus-registry registry eieio-base gnus-art mm-uu mml2015
mm-view mml-smime smime dig mailcap gnus-sum gnus-group gnus-undo
smtpmail sendmail gnus-start gnus-cloud nnimap nnmail mail-source tls
utf7 netrc nnoo parse-time gnus-spec gnus-int gnus-range gnus-win gnus
gnus-ems nnheader message rfc822 mml mml-sec mm-decode mm-bodies
mm-encode mail-parse rfc2231 rfc2047 rfc2045 ietf-drums mailabbrev
mail-utils gmm-utils mailheader goto-addr add-log dired-aux gud
org-element org-rmail org-mhe org-irc org-info org-gnus org-docview
doc-view jka-compr image-mode dired org-bibtex bibtex org-bbdb org-w3m
org org-macro org-footnote org-pcomplete org-list org-faces org-entities
noutline outline org-version ob-emacs-lisp ob ob-tangle ob-ref ob-lob
ob-table ob-exp org-src ob-keys ob-comint ob-core ob-eval org-compat
org-macs org-loaddefs conf-mode python sgml-mode vc-arch vc-mtn vc-hg
vc-bzr vc-sccs vc-svn vc-cvs vc-rcs js json smerge-mode diff-mode
easy-mmode bug-reference vc-git cc-mode cc-fonts cc-guess cc-menus
cc-cmds flyspell ispell eldoc diminish appt diary-lib diary-loaddefs
cal-menu calendar cal-loaddefs which-func imenu projectile edmacro
kmacro pkg-info find-func lisp-mnt epl grep compile dash s minimap
autorevert filenotify desktop frameset cus-start cus-load status
erc-services erc-list erc-menu erc-join erc-ring erc-networks
erc-pcomplete pcomplete erc-track erc-match erc-button wid-edit
cl-loaddefs cl-lib erc-fill erc-stamp erc-netsplit erc-goodies erc
erc-backend erc-compat format-spec auth-source eieio byte-opt bytecomp
byte-compile cconv eieio-core gnus-util mm-util mail-prsvr
password-cache thingatpt pp advice help-fns vc-dir ewoc vc vc-dispatcher
cc-styles cc-align cc-engine cc-vars cc-defs bbdb timezone ange-ftp
comint ansi-color ring server savehist dwarf-mode-autoloads
jabber-autoloads lisppaste-autoloads pydoc-info-autoloads info-look info
easymenu weblogger-autoloads package epg-config bbdb-autoloads time-date
tooltip electric uniquify ediff-hook vc-hooks lisp-float-type mwheel
x-win x-dnd tool-bar dnd fontset image regexp-opt fringe tabulated-list
newcomment elisp-mode lisp-mode prog-mode register page menu-bar
rfn-eshadow timer select scroll-bar mouse jit-lock font-lock syntax
facemenu font-core frame cham georgian utf-8-lang misc-lang vietnamese
tibetan thai tai-viet lao korean japanese hebrew greek romanian slovak
czech european ethiopic indian cyrillic chinese case-table epa-hook
jka-cmpr-hook help simple abbrev minibuffer nadvice loaddefs button
faces cus-face macroexp files text-properties overlay sha1 md5 base64
format env code-pages mule custom widget hashtable-print-readable
backquote make-network-process dbusbind gfilenotify dynamic-setting
system-font-setting font-render-setting move-toolbar gtk x-toolkit x
multi-tty emacs)

Memory information:
((conses 16 2621370 283762)
 (symbols 48 136643 5)
 (miscs 40 63207 11870)
 (strings 32 404536 58294)
 (string-bytes 1 18990197)
 (vectors 16 116801)
 (vector-slots 8 2921863 191230)
 (floats 8 758 1153)
 (intervals 56 289543 1523)
 (buffers 976 821)
 (heap 1024 349107 75634))

Tom




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#18721; Package emacs. (Tue, 14 Oct 2014 23:55:02 GMT) Full text and rfc822 format available.

Message #8 received at 18721 <at> debbugs.gnu.org (full text, mbox):

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: Tom Tromey <tom <at> tromey.com>
Cc: 18721 <at> debbugs.gnu.org
Subject: Re: bug#18721: 25.0.50; choose major mode from .dir-locals.el
Date: Tue, 14 Oct 2014 19:54:41 -0400
> It would be nice if there were a way to associate
> file extensions with major modes in .dir-locals.el.
> This would let projects use project-specific extensions
> without requiring per-file mode cookies or having
> everybody modify their .emacs

Indeed.  Binding .tex to latex-mode would be a common use over here.


        Stefan




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#18721; Package emacs. (Thu, 16 Oct 2014 13:24:01 GMT) Full text and rfc822 format available.

Message #11 received at 18721 <at> debbugs.gnu.org (full text, mbox):

From: Ted Zlatanov <tzz <at> lifelogs.com>
To: Stefan Monnier <monnier <at> iro.umontreal.ca>
Cc: Tom Tromey <tom <at> tromey.com>, 18721 <at> debbugs.gnu.org
Subject: Re: bug#18721: 25.0.50; choose major mode from .dir-locals.el
Date: Thu, 16 Oct 2014 09:23:06 -0400
On Tue, 14 Oct 2014 19:54:41 -0400 Stefan Monnier <monnier <at> iro.umontreal.ca> wrote: 

>> It would be nice if there were a way to associate
>> file extensions with major modes in .dir-locals.el.
>> This would let projects use project-specific extensions
>> without requiring per-file mode cookies or having
>> everybody modify their .emacs

SM> Indeed.  Binding .tex to latex-mode would be a common use over here.

I would use that, definitely.

Ted




Merged 18188 18721. Request was from Noam Postavsky <npostavs <at> users.sourceforge.net> to control <at> debbugs.gnu.org. (Wed, 15 Jun 2016 03:41:01 GMT) Full text and rfc822 format available.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#18721; Package emacs. (Sun, 15 Jan 2017 02:39:02 GMT) Full text and rfc822 format available.

Message #16 received at 18721 <at> debbugs.gnu.org (full text, mbox):

From: Tom Tromey <tom <at> tromey.com>
To: 18721 <at> debbugs.gnu.org
Subject: patch
Date: Sat, 14 Jan 2017 19:38:02 -0700
This patch adds the feature described in this bug.  There are a few
possible ways to do it, this picks a simple one: mimic auto-mode-alist
directly.

Let me know what you think.

thanks,
Tom

commit e13f5fdb86142e1b5bc3e8315a4219287da9c55b
Author: Tom Tromey <tom <at> tromey.com>
Date:   Fri Jan 13 11:10:08 2017 -0700

    Add auto-mode-alist functionality to .dir-locals.el
    
    Bug#18721
    * doc/emacs/custom.texi (Directory Variables): Document
    auto-mode-alist in .dir-locals.el.
    * doc/emacs/modes.texi (Choosing Modes): Update.
    * lisp/files.el (set-auto-mode--apply-alist): New function,
    from set-auto-mode.
    (set-auto-mode): Check directory locals for auto-mode-alist.
    (dir-locals-collect-variables): Add "predicate" parameter.
    (hack-dir-local--get-variables): New function, from
    hack-dir-local-variables.
    (hack-dir-local-variables): Call hack-dir-local--get-variables.
    * test/data/files-resources/.dir-locals.el: New file.
    * test/data/files-resources/whatever.quux: New file.
    * test/lisp/files-tests.el (files-tests-data-dir): New variable.
    (files-test-dir-locals-auto-mode-alist): New test.

diff --git a/doc/emacs/custom.texi b/doc/emacs/custom.texi
index c84f4a9..2386cfd 100644
--- a/doc/emacs/custom.texi
+++ b/doc/emacs/custom.texi
@@ -1351,6 +1351,16 @@ Directory Variables
 cannot be specified as a directory local variable.  @xref{File
 Variables}.
 
+The special key @code{auto-mode-alist} in a @file{.dir-locals.el} lets
+you set a file's major mode.  It works much like the variable
+@code{auto-mode-alist} (@pxref{Choosing Modes}).  For example, here is
+how you can tell Emacs that @file{.def} source files in this directory
+should be in C mode:
+
+@example
+((auto-mode-alist . (("\\.def\\'" . c-mode))))
+@end example
+
 @findex add-dir-local-variable
 @findex delete-dir-local-variable
 @findex copy-file-locals-to-dir-locals
diff --git a/doc/emacs/modes.texi b/doc/emacs/modes.texi
index 0acb82d..04736a3 100644
--- a/doc/emacs/modes.texi
+++ b/doc/emacs/modes.texi
@@ -349,8 +349,12 @@ Choosing Modes
 particular file type, it is better to enable the minor mode via a
 major mode hook (@pxref{Major Modes}).
 
+  Second, Emacs checks whether the file's extension matches an entry
+in any directory-local @code{auto-mode-alist}.  These are found using
+the @file{.dir-locals.el} facility (@pxref{Directory Variables}).
+
 @vindex interpreter-mode-alist
-  Second, if there is no file variable specifying a major mode, Emacs
+  Third, if there is no file variable specifying a major mode, Emacs
 checks whether the file's contents begin with @samp{#!}.  If so, that
 indicates that the file can serve as an executable shell command,
 which works by running an interpreter named on the file's first line
@@ -368,7 +372,7 @@ Choosing Modes
 @samp{'\"} to specify a list of troff preprocessors.
 
 @vindex magic-mode-alist
-  Third, Emacs tries to determine the major mode by looking at the
+  Fourth, Emacs tries to determine the major mode by looking at the
 text at the start of the buffer, based on the variable
 @code{magic-mode-alist}.  By default, this variable is @code{nil} (an
 empty list), so Emacs skips this step; however, you can customize it
@@ -396,7 +400,7 @@ Choosing Modes
 beginning of the buffer; if the function returns non-@code{nil}, Emacs
 set the major mode with @var{mode-function}.
 
-  Fourth---if Emacs still hasn't found a suitable major mode---it
+  Fifth---if Emacs still hasn't found a suitable major mode---it
 looks at the file's name.  The correspondence between file names and
 major modes is controlled by the variable @code{auto-mode-alist}.  Its
 value is a list in which each element has this form,
diff --git a/lisp/files.el b/lisp/files.el
index b57e35b..6a59077 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -2808,11 +2808,62 @@ magic-mode-regexp-match-limit
   "Upper limit on `magic-mode-alist' regexp matches.
 Also applies to `magic-fallback-mode-alist'.")
 
+(defun set-auto-mode--apply-alist (alist keep-mode-if-same dir-local)
+  "Helper function for `set-auto-mode'.
+This function takes an alist of the same form as
+`auto-mode-alist'.  It then tries to find the appropriate match
+in the alist for the current buffer; setting the mode if
+possible.  Returns non-`nil' if the mode was set, `nil'
+otherwise.  DIR-LOCAL is a boolean which, if true, says that this
+call is via directory-locals and extra checks should be done."
+  (if buffer-file-name
+      (let (mode
+            (name buffer-file-name)
+            (remote-id (file-remote-p buffer-file-name))
+            (case-insensitive-p (file-name-case-insensitive-p
+                                 buffer-file-name)))
+        ;; Remove backup-suffixes from file name.
+        (setq name (file-name-sans-versions name))
+        ;; Remove remote file name identification.
+        (when (and (stringp remote-id)
+                   (string-match (regexp-quote remote-id) name))
+          (setq name (substring name (match-end 0))))
+        (while name
+          ;; Find first matching alist entry.
+          (setq mode
+                (if case-insensitive-p
+                    ;; Filesystem is case-insensitive.
+                    (let ((case-fold-search t))
+                      (assoc-default alist 'string-match))
+                  ;; Filesystem is case-sensitive.
+                  (or
+                   ;; First match case-sensitively.
+                   (let ((case-fold-search nil))
+                     (assoc-default name alist 'string-match))
+                   ;; Fallback to case-insensitive match.
+                   (and auto-mode-case-fold
+                        (let ((case-fold-search t))
+                          (assoc-default name alist 'string-match))))))
+          (if (and mode
+                   (consp mode)
+                   (cadr mode))
+              (setq mode (car mode)
+                    name (substring name 0 (match-beginning 0)))
+            (setq name nil)))
+        (when (and dir-local mode)
+          (unless (string-suffix-p "-mode" (symbol-name mode))
+            (message "Ignoring invalid mode `%s'" (symbol-name mode))
+            (setq mode nil)))
+        (when mode
+          (set-auto-mode-0 mode keep-mode-if-same)
+          t))))
+
 (defun set-auto-mode (&optional keep-mode-if-same)
   "Select major mode appropriate for current buffer.
 
 To find the right major mode, this function checks for a -*- mode tag
 checks for a `mode:' entry in the Local Variables section of the file,
+checks if there an `auto-mode-alist' entry in `.dir-locals.el',
 checks if it uses an interpreter listed in `interpreter-mode-alist',
 matches the buffer beginning against `magic-mode-alist',
 compares the filename against the entries in `auto-mode-alist',
@@ -2869,6 +2920,14 @@ set-auto-mode
 	      (or (set-auto-mode-0 mode keep-mode-if-same)
 		  ;; continuing would call minor modes again, toggling them off
 		  (throw 'nop nil))))))
+    ;; Check for auto-mode-alist entry in dir-locals.
+    (unless done
+      (with-demoted-errors "Directory-local variables error: %s"
+	;; Note this is a no-op if enable-local-variables is nil.
+        (let* ((mode-alist (cdr (hack-dir-local--get-variables
+                                 (lambda (key) (eq key 'auto-mode-alist))))))
+          (setq done (set-auto-mode--apply-alist mode-alist
+                                                 keep-mode-if-same t)))))
     ;; hack-local-variables checks local-enable-local-variables etc, but
     ;; we might as well be explicit here for the sake of clarity.
     (and (not done)
@@ -2917,45 +2976,8 @@ set-auto-mode
 	  (set-auto-mode-0 done keep-mode-if-same)))
     ;; Next compare the filename against the entries in auto-mode-alist.
     (unless done
-      (if buffer-file-name
-	  (let ((name buffer-file-name)
-		(remote-id (file-remote-p buffer-file-name))
-		(case-insensitive-p (file-name-case-insensitive-p
-				     buffer-file-name)))
-	    ;; Remove backup-suffixes from file name.
-	    (setq name (file-name-sans-versions name))
-	    ;; Remove remote file name identification.
-	    (when (and (stringp remote-id)
-		       (string-match (regexp-quote remote-id) name))
-	      (setq name (substring name (match-end 0))))
-	    (while name
-	      ;; Find first matching alist entry.
-	      (setq mode
-		    (if case-insensitive-p
-			;; Filesystem is case-insensitive.
-			(let ((case-fold-search t))
-			  (assoc-default name auto-mode-alist
-					 'string-match))
-		      ;; Filesystem is case-sensitive.
-		      (or
-		       ;; First match case-sensitively.
-		       (let ((case-fold-search nil))
-			 (assoc-default name auto-mode-alist
-					'string-match))
-		       ;; Fallback to case-insensitive match.
-		       (and auto-mode-case-fold
-			    (let ((case-fold-search t))
-			      (assoc-default name auto-mode-alist
-					     'string-match))))))
-	      (if (and mode
-		       (consp mode)
-		       (cadr mode))
-		  (setq mode (car mode)
-			name (substring name 0 (match-beginning 0)))
-		(setq name nil))
-	      (when mode
-		(set-auto-mode-0 mode keep-mode-if-same)
-		(setq done t))))))
+      (setq done (set-auto-mode--apply-alist auto-mode-alist
+                                             keep-mode-if-same nil)))
     ;; Next try matching the buffer beginning against magic-fallback-mode-alist.
     (unless done
       (if (setq done (save-excursion
@@ -3716,10 +3738,13 @@ dir-locals-collect-mode-variables
 	;; Need a new cons in case we setcdr later.
 	(push (cons variable value) variables)))))
 
-(defun dir-locals-collect-variables (class-variables root variables)
+(defun dir-locals-collect-variables (class-variables root variables
+                                                     &optional predicate)
   "Collect entries from CLASS-VARIABLES into VARIABLES.
 ROOT is the root directory of the project.
-Return the new variables list."
+Return the new variables list.
+If PREDICATE is given, it is used to test a symbol key in the alist
+to see whether it should be considered."
   (let* ((file-name (or (buffer-file-name)
 			;; Handle non-file buffers, too.
 			(expand-file-name default-directory)))
@@ -3737,9 +3762,11 @@ dir-locals-collect-variables
                          (>= (length sub-file-name) (length key))
                          (string-prefix-p key sub-file-name))
                 (setq variables (dir-locals-collect-variables
-                                 (cdr entry) root variables))))
-             ((or (not key)
-                  (derived-mode-p key))
+                                 (cdr entry) root variables predicate))))
+             ((if predicate
+                  (funcall predicate key)
+                (or (not key)
+                    (derived-mode-p key)))
               (let* ((alist (cdr entry))
                      (subdirs (assq 'subdirs alist)))
                 (if (or (not subdirs)
@@ -3955,13 +3982,13 @@ enable-remote-dir-locals
 
 (defvar hack-dir-local-variables--warned-coding nil)
 
-(defun hack-dir-local-variables ()
+(defun hack-dir-local--get-variables (predicate)
   "Read per-directory local variables for the current buffer.
-Store the directory-local variables in `dir-local-variables-alist'
-and `file-local-variables-alist', without applying them.
-
-This does nothing if either `enable-local-variables' or
-`enable-dir-local-variables' are nil."
+Return a cons of the form (DIR . ALIST), where DIR is the
+directory name (maybe nil) and ALIST is an alist of all variables
+that might apply.  These will be filtered according to the
+buffer's directory, but not according to its mode.
+PREDICATE is passed to `dir-locals-collect-variables'."
   (when (and enable-local-variables
 	     enable-dir-local-variables
 	     (or enable-remote-dir-locals
@@ -3980,21 +4007,33 @@ hack-dir-local-variables
 	(setq dir-name (nth 0 dir-or-cache))
 	(setq class (nth 1 dir-or-cache))))
       (when class
-	(let ((variables
-	       (dir-locals-collect-variables
-		(dir-locals-get-class-variables class) dir-name nil)))
-	  (when variables
-	    (dolist (elt variables)
-	      (if (eq (car elt) 'coding)
-                  (unless hack-dir-local-variables--warned-coding
-                    (setq hack-dir-local-variables--warned-coding t)
-                    (display-warning 'files
-                                     "Coding cannot be specified by dir-locals"))
-		(unless (memq (car elt) '(eval mode))
-		  (setq dir-local-variables-alist
-			(assq-delete-all (car elt) dir-local-variables-alist)))
-		(push elt dir-local-variables-alist)))
-	    (hack-local-variables-filter variables dir-name)))))))
+        (cons dir-name
+              (dir-locals-collect-variables
+               (dir-locals-get-class-variables class)
+               dir-name nil predicate))))))
+
+(defun hack-dir-local-variables ()
+  "Read per-directory local variables for the current buffer.
+Store the directory-local variables in `dir-local-variables-alist'
+and `file-local-variables-alist', without applying them.
+
+This does nothing if either `enable-local-variables' or
+`enable-dir-local-variables' are nil."
+  (let* ((items (hack-dir-local--get-variables nil))
+         (dir-name (car items))
+         (variables (cdr items)))
+    (when variables
+      (dolist (elt variables)
+        (if (eq (car elt) 'coding)
+            (unless hack-dir-local-variables--warned-coding
+              (setq hack-dir-local-variables--warned-coding t)
+              (display-warning 'files
+                               "Coding cannot be specified by dir-locals"))
+          (unless (memq (car elt) '(eval mode))
+            (setq dir-local-variables-alist
+                  (assq-delete-all (car elt) dir-local-variables-alist)))
+          (push elt dir-local-variables-alist)))
+      (hack-local-variables-filter variables dir-name))))
 
 (defun hack-dir-local-variables-non-file-buffer ()
   "Apply directory-local variables to a non-file buffer.
diff --git a/test/data/files-resources/.dir-locals.el b/test/data/files-resources/.dir-locals.el
new file mode 100644
index 0000000..84997b8
--- /dev/null
+++ b/test/data/files-resources/.dir-locals.el
@@ -0,0 +1,2 @@
+;; This is used by files-tests.el.
+((auto-mode-alist . (("\\.quux\\'" . tcl-mode))))
diff --git a/test/data/files-resources/whatever.quux b/test/data/files-resources/whatever.quux
new file mode 100644
index 0000000..595583b
--- /dev/null
+++ b/test/data/files-resources/whatever.quux
@@ -0,0 +1,2 @@
+# Used by files-test.el.
+# Due to .dir-locals.el this should end up in Tcl mode.
diff --git a/test/lisp/files-tests.el b/test/lisp/files-tests.el
index 9d456c5..c0dd800 100644
--- a/test/lisp/files-tests.el
+++ b/test/lisp/files-tests.el
@@ -25,6 +25,11 @@
 ;; triggered.
 (defvar files-test-result nil)
 
+;; Where supplementary files can be found.
+(defvar files-tests-data-dir
+  (expand-file-name "data/files-resources/"
+                    (getenv "EMACS_TEST_DIRECTORY")))
+
 (defvar files-test-safe-result nil)
 (put 'files-test-safe-result 'safe-local-variable 'booleanp)
 
@@ -243,5 +248,10 @@ files-test-bug-18141-file
                          (concat "/:/:" subdir)))))
       (delete-directory dir 'recursive))))
 
+(ert-deftest files-test-dir-locals-auto-mode-alist ()
+  "Test an `auto-mode-alist' entry in `.dir-locals.el'"
+  (find-file (expand-file-name "whatever.quux" files-tests-data-dir))
+  (should (eq major-mode 'tcl-mode)))
+
 (provide 'files-tests)
 ;;; files-tests.el ends here




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#18721; Package emacs. (Sun, 15 Jan 2017 15:34:02 GMT) Full text and rfc822 format available.

Message #19 received at 18721 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Tom Tromey <tom <at> tromey.com>
Cc: 18721 <at> debbugs.gnu.org
Subject: Re: bug#18721: patch
Date: Sun, 15 Jan 2017 17:33:30 +0200
> From: Tom Tromey <tom <at> tromey.com>
> Date: Sat, 14 Jan 2017 19:38:02 -0700
> 
> This patch adds the feature described in this bug.  There are a few
> possible ways to do it, this picks a simple one: mimic auto-mode-alist
> directly.
> 
> Let me know what you think.

Thanks.  This will need a NEWS entry.  Also, I have a comment/concern:

> +The special key @code{auto-mode-alist} in a @file{.dir-locals.el} lets
> +you set a file's major mode.  It works much like the variable
> address <at> hidden (@pxref{Choosing Modes}).  For example, here is
> +how you can tell Emacs that @file{.def} source files in this directory
> +should be in C mode:
> +
> +@example
> +((auto-mode-alist . (("\\.def\\'" . c-mode))))
> +@end example

I'm concerned that this syntax deviates from the current syntax of
.dir-locals.el, which is this:

  (KEY . ((VARIABLE1 . VALUE1) (VARIABLE2 . VALUE2) ...))

where KEY can be:

  . nil, meaning it's applicable to any file in the directory tree;
  . a major mode (not relevant to this discussion)
  . a subdirectory name, meaning the setting is applicable to files in
    that subdirectory

Your suggestion uses a form of just (VARIABLE . VALUE), effectively
using the variable name as KEY, which differs from the above, and also
loses the capability of specifying a value only for some subdirectory.

So I'm asking why not keep the current syntax for this feature, and
avoid introducing a new kind of special key?  This would mean your
example above will look like this:

  (nil . ((auto-mode-alist . (("\\.def\\'" . c-mode)))))




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#18721; Package emacs. (Sun, 15 Jan 2017 19:23:02 GMT) Full text and rfc822 format available.

Message #22 received at 18721 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: tom <at> tromey.com
Cc: 18721 <at> debbugs.gnu.org
Subject: Re: bug#18721: patch
Date: Sun, 15 Jan 2017 21:22:20 +0200
> Date: Sun, 15 Jan 2017 17:33:30 +0200
> From: Eli Zaretskii <eliz <at> gnu.org>
> Cc: 18721 <at> debbugs.gnu.org
> 
> Thanks.  This will need a NEWS entry.  Also, I have a comment/concern:

Oh, and one more nit: .dir-locals.el is documented to apply to remote
files if enable-remote-dir-locals is non-nil.  But, unless I
misunderstood your code, it exempts remote files unconditionally,
which contradicts the documentation.

Thanks.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#18721; Package emacs. (Mon, 16 Jan 2017 19:42:01 GMT) Full text and rfc822 format available.

Message #25 received at 18721 <at> debbugs.gnu.org (full text, mbox):

From: Tom Tromey <tom <at> tromey.com>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: Tom Tromey <tom <at> tromey.com>, 18721 <at> debbugs.gnu.org
Subject: Re: bug#18721: patch
Date: Mon, 16 Jan 2017 12:17:11 -0700
>>>>> "Eli" == Eli Zaretskii <eliz <at> gnu.org> writes:

Eli> So I'm asking why not keep the current syntax for this feature, and
Eli> avoid introducing a new kind of special key?  This would mean your
Eli> example above will look like this:
Eli>   (nil . ((auto-mode-alist . (("\\.def\\'" . c-mode)))))

I suppose it would work ok as long as the code also doesn't try to set
auto-mode-alist locally in the buffer, both because that variable isn't
safe, and because I think it doesn't make sense to set it buffer-locally
anyhow.

Eli> Oh, and one more nit: .dir-locals.el is documented to apply to
Eli> remote files if enable-remote-dir-locals is non-nil.  But, unless I
Eli> misunderstood your code, it exempts remote files unconditionally,
Eli> which contradicts the documentation.

I think this should work ok because hack-dir-local--get-variables checks
enable-remote-dir-locals.  In this case it will return nil, so the new
code in set-auto-mode will not do anything.

Tom




Added tag(s) patch. Request was from Lars Ingebrigtsen <larsi <at> gnus.org> to control <at> debbugs.gnu.org. (Sat, 29 May 2021 04:07:01 GMT) Full text and rfc822 format available.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#18721; Package emacs. (Sat, 29 May 2021 04:08:02 GMT) Full text and rfc822 format available.

Message #30 received at 18721 <at> debbugs.gnu.org (full text, mbox):

From: Lars Ingebrigtsen <larsi <at> gnus.org>
To: Tom Tromey <tom <at> tromey.com>
Cc: Eli Zaretskii <eliz <at> gnu.org>, 18721 <at> debbugs.gnu.org, 18188 <at> debbugs.gnu.org
Subject: Re: bug#18188: Please make it possible to have a directory-local
 setting for auto-mode-alist
Date: Sat, 29 May 2021 06:07:40 +0200
Tom Tromey <tom <at> tromey.com> writes:

> Eli> So I'm asking why not keep the current syntax for this feature, and
> Eli> avoid introducing a new kind of special key?  This would mean your
> Eli> example above will look like this:
> Eli>   (nil . ((auto-mode-alist . (("\\.def\\'" . c-mode)))))
>
> I suppose it would work ok as long as the code also doesn't try to set
> auto-mode-alist locally in the buffer, both because that variable isn't
> safe, and because I think it doesn't make sense to set it buffer-locally
> anyhow.

I think everybody agreed that this was a good feature to add (with Eli's
tweaks), but as far as I can tell, it was never added.  Tom, would it be
possible for you to re-spin this patch for Emacs 28?

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#18721; Package emacs. (Sat, 29 May 2021 15:15:02 GMT) Full text and rfc822 format available.

Message #33 received at 18721 <at> debbugs.gnu.org (full text, mbox):

From: Tom Tromey <tom <at> tromey.com>
To: Lars Ingebrigtsen <larsi <at> gnus.org>
Cc: Eli Zaretskii <eliz <at> gnu.org>, Tom Tromey <tom <at> tromey.com>,
 18721 <at> debbugs.gnu.org, 18188 <at> debbugs.gnu.org
Subject: Re: bug#18188: Please make it possible to have a directory-local
 setting for auto-mode-alist
Date: Sat, 29 May 2021 09:13:58 -0600
Lars> I think everybody agreed that this was a good feature to add (with Eli's
Lars> tweaks), but as far as I can tell, it was never added.  Tom, would it be
Lars> possible for you to re-spin this patch for Emacs 28?

Doubtful, but it's fine by me if someone takes it over.  I don't recall
who objected or why, but someone didn't like the approach I took and
requested changes, so whoever does it would have to navigate this.

Tom




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#18721; Package emacs. (Fri, 23 Jul 2021 14:01:02 GMT) Full text and rfc822 format available.

Message #36 received at 18721 <at> debbugs.gnu.org (full text, mbox):

From: Lars Ingebrigtsen <larsi <at> gnus.org>
To: Tom Tromey <tom <at> tromey.com>
Cc: Eli Zaretskii <eliz <at> gnu.org>, 18721 <at> debbugs.gnu.org, 18188 <at> debbugs.gnu.org
Subject: Re: bug#18721: 25.0.50; choose major mode from .dir-locals.el
Date: Fri, 23 Jul 2021 16:00:11 +0200
Tom Tromey <tom <at> tromey.com> writes:

> Lars> I think everybody agreed that this was a good feature to add (with Eli's
> Lars> tweaks), but as far as I can tell, it was never added.  Tom, would it be
> Lars> possible for you to re-spin this patch for Emacs 28?
>
> Doubtful, but it's fine by me if someone takes it over.  I don't recall
> who objected or why, but someone didn't like the approach I took and
> requested changes, so whoever does it would have to navigate this.

I've now respun the patch for Emacs 28, and it seems to work fine, and I
think everybody agreed that this was useful functionality.

There was some discussion about whether to introduce a new dir-locals
element for this, or just use the normal variable-setting thing.  As Tom
said, the normal thing is awkward to use here (because of the
buffer-local thing), so I think Tom's approach here is the best,
overall.

So I've now pushed this to Emacs 28.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no




bug marked as fixed in version 28.1, send any further explanations to 18721 <at> debbugs.gnu.org and Tom Tromey <tom <at> tromey.com> Request was from Lars Ingebrigtsen <larsi <at> gnus.org> to control <at> debbugs.gnu.org. (Fri, 23 Jul 2021 14:01:02 GMT) Full text and rfc822 format available.

bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Sat, 21 Aug 2021 11:24:05 GMT) Full text and rfc822 format available.

This bug report was last modified 2 years and 242 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.