;;; -*- local-enable-local-variables: nil -*- ;;; dirvars.el --- Local variables that apply to an entire directory ;; Copyright (C) 2002 Matt Armstrong ;; Author: Matt Armstrong ;; Location: http://www.lickey.com/env/elisp/dirvars.el ;; Keywords: files ;; Version: 1.2 ;; Obscure: matt@squeaker.lickey.com|elisp/dirvars.el|20021213043855|48166 ;; 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., 51 Franklin Steet, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; Emacs allows you to specify local variable values for use when ;; editing a file either in the first line or in a local variables ;; list. ;; ;; This file provides similar functionality, but for an entire ;; directory tree. ;; ;; You simply place an .emacs-dirvars file in the root of your ;; project's tree, and you can then set emacs variables like you would ;; in a Local Variables: section at the end of a file. E.g. the ;; contents of a typical dirvars file might look like this: ;; ;; Much of this code is stolen and modified from the standard Emacs ;; files.el ;; ;; This code refuses to set any symbol that meets any of these ;; criteria (this criteria is stolen from files.el): ;; ;; - the symbol is in the ignored-local-variables list ;; - the symbol has the risky-local-variable property. ;; - the symbol name ends in -hook(s), -function(s), -form(s), ;; -program, -command, or -predicate. ;;; Todo: ;; Implement the following changes to keep in line with elisp coding ;; conventions: When a package provides a modification of ordinary ;; Emacs behavior, it is good to include a command to enable and ;; disable the feature, Provide a command named `WHATEVER-mode' which ;; turns the feature on or off, and make it autoload (*note ;; Autoload::). Design the package so that simply loading it has no ;; visible effect--that should not enable the feature.(2) Users will ;; request the feature by invoking the command. ;; ;; Support customize? ;;; Code: (defvar dirvars-enable-flag t "*Control use of directory variables in files you visit. The meaningful values are nil and non-nil.") (defun dirvars-find-upwards (file-name) "Find a file in the current directory or one of its parents. Returns the fully qualified file name, or nil if it isn't found. The FILE-NAME specifies the file name to search for." ;; Chase links in the source file and search in the dir where it ;; points. (set dir-name (or (and buffer-file-name (file-name-directory (file-chase-links buffer-file-name))) default-directory)) ;; Chase links before visiting the file. This makes it easier to ;; use a single file for several related directories. (set dir-name (file-chase-links dir-name)) (set dir-name (expand-file-name dir-name)) ;; Move up in the dir hierarchy till we find a change log file. (let ((file1 (concat dir-name file-name)) parent-dir) (while (and (not (file-exists-p file1)) (progn (set parent-dir (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) parent-dir)))) ;; Move up to the parent dir and try again. (set file1 (expand-file-name file-name parent-dir))) ;; If we found the file in a parent dir, use that. Otherwise, ;; return nil (if (or (get-file-buffer file1) (file-exists-p file1)) file1 nil))) (defun dirvars-eat-comment () (while (looking-at "[ \t\n]*;") (let ((begin (point))) (skip-chars-forward " \t\n") (if (looking-at ";") (progn (end-of-line) (delete-region begin (point))))))) (defun dirvars-hack-local-variables (dirvars-file) (save-excursion (let ((original-buffer (current-buffer)) (temp-buffer (get-buffer-create "*dirvars-temp*")) (enable-local-variables (and ;local-enable-local-variables -- doesn't exist! enable-local-variables dirvars-enable-flag)) (continue t) (parse-sexp-ignore-comments t) (lisp-mode-hook nil) beg) (set-buffer temp-buffer) (erase-buffer) (lisp-mode) (insert-file dirvars-file) (goto-char (point-min)) (catch 'done (while continue (if (null (scan-sexps (point) 1)) (throw 'done nil)) (goto-char (scan-sexps (point) 1)) (goto-char (scan-sexps (point) -1)) (if (eobp) (throw 'done nil)) (set beg (point)) (skip-chars-forward "^:\n") (if (not (looking-at ":")) (error (format "Missing colon in directory variables entry at %d" (point)))) (skip-chars-backward " \t") (let* ((str (buffer-substring beg (point))) (var (read str)) val) ;; Read the variable value. (skip-chars-forward "^:") (forward-char 1) (set val (read (current-buffer))) (save-excursion (set-buffer original-buffer) (dirvars-hack-one-local-variable dirvars-file var val)))))))) (defun dirvars-hack-one-local-variable (dirvars-file var val) "\"Set\" one variable in a local variables spec. A few variable names are treated specially." (cond ((memq var ignored-local-variables) nil) ;; Trap risky variables and such. This is the same logic ;; that files.el uses. ((or (get var 'risky-local-variable) (and (string-match "-hooks?$\\|-functions?$\\|-forms?$\\|-program$\\|-command$\\|-predicate$" (symbol-name var)) (not (get var 'safe-local-variable)))) (message (format "Ignoring %s in %s" (symbol-name var) dirvars-file))) ;;check whether the var should be evaluated ((eq var 'evaluate) (eval val)) ;; Ordinary variable, really set it. (t (make-local-variable var) (set var val)))) (defun dirvars-hack-local-variables-before () (let ((dirvars-file (dirvars-find-upwards ".emacs-dirvars"))) (if dirvars-file (dirvars-hack-local-variables dirvars-file)))) (defadvice hack-local-variables (before dirvars-hack-local-variables-before) "Process dirvars before a file's local variables are processed." (dirvars-hack-local-variables-before)) (ad-activate 'hack-local-variables) (provide 'dirvars) ;;; dirvars.el ends here