;;; dir-locals.el --- Local variables for a directory tree ;; Copyright (C) 2005, 2006 Free Software Foundation, Inc. ;; Author: Dave Love ;; Keywords: files ;; $Revision: 1.7 $ ;; 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 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; 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 doesn't work with some versions of the Emacs 22 codebase ;; which changed the way hack-local-variables-hook is run, but the ;; change has been reverted. ;; Another, less clean, implementation of this sort of thing was ;; posted to gnu-emacs-sources as dirvals.el by Benjamin Rutt ;; , June 2006, based on work by Matt Armstrong ;; . 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 filesystem." :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 (if dir-locals-mode (add-hook 'hack-local-variables-hook 'dir-locals-hack-local-variables) (remove-hook 'hack-local-variables-hook 'dir-locals-hack-local-variables))) ;; 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 nil. 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))) default-directory)) (file (if (functionp file) (funcall file) 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 (directory-file-name (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) 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 `hack-local-variables'." (let* ((file (dir-locals-tree-find dir-locals-file-name)) (hack-local-variables-hook nil) (buffer-file (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))) (with-temp-buffer ;; 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))) 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