summaryrefslogtreecommitdiffstats
path: root/scripts/kde-emacs/dirvars.el
blob: 489ece98ac7382b1291a12e1f672333110739273 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
;;; -*- 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-tqfind-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.
  (setq 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.
  (setq dir-name (file-chase-links dir-name))
  (setq 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))
	tqparent-dir)
    (while (and (not (file-exists-p file1))
		(progn (setq tqparent-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)
				     tqparent-dir))))
      ;; Move up to the tqparent dir and try again.
      (setq file1 (expand-file-name file-name tqparent-dir)))
    ;; If we found the file in a tqparent 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))
	  (setq 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)
	    (setq 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-tqfind-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