[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gmane.emacs.sources] dir-locals.el (was: dirvars.el 1.3)

From: Kim F. Storm
Subject: [gmane.emacs.sources] dir-locals.el (was: dirvars.el 1.3)
Date: Tue, 20 Jun 2006 13:13:54 +0200
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/22.0.50 (gnu/linux)

Dave Love has written a clean alternative to the "dirvars" package
posted recently, but recent (major) changes to hack-local-variables
broke his code.

Before, hack-local-variables-hook was run unconditionally at the end
of hack-local-variables, but now it is only run if the file has local

Is that change intentional or just an oversight?

Here is Dave's code:

;;; dir-locals.el --- Local variables for a directory tree

;; Copyright (C) 2005, 2006  Free Software Foundation, Inc.

;; Author: Dave Love <address@hidden>
;; Keywords: files
;; $Revision: 1.6 $
;; URL: http://www.loveshack.ukfsn.org/emacs

;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;;; Commentary:

;; It can be useful to specify local variables directory-wide, e.g. to
;; define CC mode styles consistently.  This library implements such a
;; scheme, controlled by the global minor mode `dir-locals-mode'.

;; Place a file named `.emacs-locals' (or the value of
;; `dir-locals-file-name') in the directory root.  This should specify
;; local variables in the usual way.  The values it sets are inherited
;; when a file in the directory tree is found.  Local variables
;; specified in the found file override the directory-wide ones.

;; However, `eval' pseudo-variables specified in the file are
;; evaluated (assuming `enable-local-eval' is true) _before_ any
;; directory-wide processing, and they are evaluated in a scratch
;; buffer, so that they are only useful for side effects on local
;; variables.  `mode' pseudo-variables which specify minor modes
;; toggle those modes for files within the directory.  If
;; .emacs-locals specifies a major mode, it doesn't propagate, but any
;; local variables and minor modes its hook sets will; thus it should
;; normally not specify a major mode.  The `coding' pseudo-variable
;; will not propagate from .emacs-locals.

;; For example, with dir-locals mode on, placing this in .emacs-locals
;; at the top-level of the Linux source tree would set the C
;; indentation style appropriately for files within the tree:
;;   Local variables:
;;   c-file-style: "linux"
;;   End:
;; (and ignore the stupid remarks in Documentation/CodingStyle).

;; Another possible use is, say, setting change-log parameters in
;; different trees for which the Emacs 22 development source broke use
;; of change-log-mode-hook.

;; NB: this no longer works properly with the Emacs 22 codebase since
;; that changed the way hack-local-variables-hook is run; sigh.  In
;; that case it falls back to using `find-file-hook', which doesn't
;; really do the right thing, but should mostly work OK.

;; Another, less clean, implementation of this sort of thing was
;; posted to gnu-emacs-sources as dirvals.el by Benjamin Rutt
;; <address@hidden>, June 2006, based on work by Matt Armstrong
;; <address@hidden>.  It uses a different format for the equivalent
;; of .emacs-locals.

;;; Code:

(defgroup dir-locals ()
  "Directory-wide file-local variables"
  :link '(emacs-commentary-link "dir-locals")
  :group 'files)

(defcustom dir-locals-file-name ".emacs-locals"
  "File name used by Dir-Locals mode to specify local variables.
This should specify local variables in the normal way.  When Dir-Locals
minor mode is active, these will be inherited by files found in a
directory tree containing such a file at its root.

This may also be a function of no arguments which returns the name to
use, allowing arbitrary per-directory customization of the
per-directory customization file on the basis of `default-directory'."
  :group 'dir-locals
  :type '(choice file function))

;; Adapted from dirvals.el.
(defcustom dir-locals-chase-remote nil
  "Non-nil means search upwards for `dir-locals-file-name' in remote 
  :group 'dir-locals
  :type 'boolean)

(define-minor-mode dir-locals-mode
  "Toggle use of directory-wide file-local variables.
See `dir-locals-file-name'."
  :global t
  ;; Emacs 22 doesn't run `hack-local-variables-hook' if the file has
  ;; no local variables; sigh.  Using this new hook at least doesn't
  ;; catch the case of just changing the major mode, but mostly works.
  (if (boundp 'find-file-hook)
      (if dir-locals-mode
          (add-hook 'find-file-hook 'dir-locals-hack-local-variables)
        (remove-hook 'find-file-hook 'dir-locals-hack-local-variables))
    (if dir-locals-mode
        (add-hook 'hack-local-variables-hook 'dir-locals-hack-local-variables)
      (remove-hook 'hack-local-variables-hook

;; Following find-change-log.  Fixme:  Should be abstracted from there.
(defun dir-locals-tree-find (file)
  "Find FILE in the current directory or one of its parents.
If one is found, return its fully-qualified name, otherwise return

FILE may be a string or a nullary function returning one on the basis
of `default-directory'."
  (unless (and (not dir-locals-chase-remote)
               (fboundp 'file-remote-p) ; not in Emacs 21
               (file-remote-p default-directory))
    (let* ((dir-name
            ;; Chase links in the source file and start searching in
            ;; the dir where it resides.
            (or (if buffer-file-name
                    (file-name-directory (file-chase-links buffer-file-name)))
           (file (if (functionp file)
                     (funcall file)
           (file1 (if (file-directory-p dir-name)
                      (expand-file-name file dir-name))))
      ;; Chase links before visiting the file.  This makes it easier
      ;; to use a file for several related directories.
      (setq file1 (expand-file-name (file-chase-links file1)))
      ;; Move up in the dir hierarchy till we find a suitable file.
      (while (and (not (file-exists-p file1))
                  (setq dir-name (file-name-directory
                                   (file-name-directory file1))))
                  ;; Give up if we are already at the root dir.
                  (not (string= (file-name-directory file1) dir-name)))
        ;; Move up to the parent dir and try again.
        (setq file1 (expand-file-name (file-name-nondirectory file) dir-name)))
      (if (file-exists-p file1)

(defun dir-locals-hack-local-variables ()
  "Set local variables from directory-wide values.
Inherit the local variables set in `dir-locals-file-name' if that is
found by `dir-locals-tree-find'.  Ignore everything ignored by
  (let* ((file (dir-locals-tree-find dir-locals-file-name))
         (hack-local-variables-hook nil)
          (if buffer-file-name
              (expand-file-name (file-chase-links buffer-file-name))))
         ;; Fixme:  Probably condition-case this and ensure any error
         ;; messages indicate the directory file.
         (vars (when (and file
                          ;; Don't do it twice, so as to avoid
                          ;; repeating possible interactive queries.
                          (not (equal file buffer-file)))
                   ;; Make queries from `hack-local-variables' clearer.
                   (rename-buffer (file-name-nondirectory file) t)
                   (insert-file-contents file)
                   (let* ((locals (buffer-local-variables))
                          (_ (hack-local-variables))
                         (new-locals (buffer-local-variables)))
                     ;; Derive the list of new pairs.
                     (dolist (l locals)
                       (setq new-locals (delete l new-locals)))
                     ;; And some internals which get updated.
                     (dolist (l '(buffer-display-time buffer-display-count))
                       (setq new-locals (assq-delete-all l new-locals)))
    (dolist (v vars)
      (let ((sym (car v)))
        (unless (local-variable-p sym)  ; file-locals take precedence
          (if (and (string-match "-mode\\'" (symbol-name sym))
                   (fboundp sym))
              (funcall sym)
            (set (make-local-variable sym) (cdr v))))))))

(provide 'dir-locals)

;;; dir-locals.el ends here

Kim F. Storm <address@hidden> http://www.cua.dk

reply via email to

[Prev in Thread] Current Thread [Next in Thread]