|
|
|
;;; -*- local-enable-local-variables: nil -*-
|
|
|
|
;;; dirvars.el --- Local variables that apply to an entire directory
|
|
|
|
|
|
|
|
;; Copyright (C) 2002 Matt Armstrong
|
|
|
|
|
|
|
|
;; Author: Matt Armstrong <matt@lickey.com>
|
|
|
|
;; 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:
|
|
|
|
;;
|
|
|
|
;; ;; -*- emacs-lisp -*-
|
|
|
|
;; ;;
|
|
|
|
;; ;; This file is processed by the dirvars emacs package. Each variable
|
|
|
|
;; ;; setting below is performed when this dirvars file is loaded.
|
|
|
|
;; ;;
|
|
|
|
;; indent-tabs-mode: nil
|
|
|
|
;; tab-width: 8
|
|
|
|
;; show-trailing-whitespace: t
|
|
|
|
;; indicate-empty-lines: t
|
|
|
|
;;
|
|
|
|
;; 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
|