aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Olson2008-05-19 22:36:09 +0000
committerMichael Olson2008-05-19 22:36:09 +0000
commit1b21ee065d77baee6da0c46a207bfb44d7cc0a95 (patch)
treeac173ee06682f6fe2d09403c2be5d246b9d26558
parentad97b375e8189e1826d562898ea78e4f3bb94bda (diff)
downloademacs-1b21ee065d77baee6da0c46a207bfb44d7cc0a95.tar.gz
emacs-1b21ee065d77baee6da0c46a207bfb44d7cc0a95.zip
Implement Project-local variables.
-rw-r--r--doc/emacs/custom.texi60
-rw-r--r--etc/NEWS3
-rw-r--r--lisp/files.el260
3 files changed, 279 insertions, 44 deletions
diff --git a/doc/emacs/custom.texi b/doc/emacs/custom.texi
index e270f863253..a85925c0f05 100644
--- a/doc/emacs/custom.texi
+++ b/doc/emacs/custom.texi
@@ -796,6 +796,7 @@ here we describe other aspects of Emacs variables.
796 of Emacs to run on particular occasions. 796 of Emacs to run on particular occasions.
797* Locals:: Per-buffer values of variables. 797* Locals:: Per-buffer values of variables.
798* File Variables:: How files can specify variable values. 798* File Variables:: How files can specify variable values.
799* Directory Variables:: How variable values can be specified by directory.
799@end menu 800@end menu
800 801
801@node Examining 802@node Examining
@@ -1262,6 +1263,65 @@ customizable list of eval forms which are safe. Emacs does not ask
1262for confirmation when it finds these forms for the @code{eval} 1263for confirmation when it finds these forms for the @code{eval}
1263variable. 1264variable.
1264 1265
1266@node Directory Variables
1267@subsection Per-Directory Local Variables
1268@cindex local variables in directories
1269@cindex directory local variables
1270
1271 Emacs provides a way to specify local variable values per-directory.
1272This can be done one of two ways.
1273
1274 The first approach is to put a special file, named
1275@file{.dir-settings.el}, in a directory. When opening a file, Emacs
1276searches for @file{.dir-settings.el} starting in the file's directory
1277and then moving up the directory hierarchy. If
1278@file{.dir-settings.el} is found, Emacs applies variable settings from
1279the file to the new buffer. If the file is remote, Emacs skips this
1280search, because it would be too slow.
1281
1282 The file should hold a specially-constructed list. This list maps
1283Emacs mode names (symbols) to alists; each alist maps variable names
1284to values. The special mode name @samp{nil} means that the alist
1285should be applied to all buffers. Finally, a string key can be used
1286to specify an alist which applies to a relative subdirectory in the
1287project.
1288
1289@example
1290((nil . ((indent-tabs-mode . t)
1291 (tab-width . 4)
1292 (fill-column . 80)))
1293 (c-mode . ((c-file-style . "BSD")))
1294 (java-mode . ((c-file-style . "BSD")))
1295 ("src/imported"
1296 . ((nil . ((change-log-default-name . "ChangeLog.local"))))))
1297@end example
1298
1299 This example shows some settings for a hypothetical project. This
1300sets @samp{indent-tabs-mode} to @samp{t} for any file in the source
1301tree, and it sets the indentation style for any C or Java source file
1302to @samp{BSD}. Finally, it specifies a different @file{ChangeLog}
1303file name for any file in the project that appears beneath the
1304directory @file{src/imported}.
1305
1306 The second approach to directory-local settings is to explicitly
1307define a project class using @code{define-project-bindings}, and then
1308to tell Emacs which directory roots correspond to that class, using
1309@code{set-directory-project}. You can put calls to these functions in
1310your @file{.emacs}; this can useful when you can't put
1311@file{.dir-settings.el} in the directory for some reason. For
1312example, you could apply settings to an unwriteable directory this
1313way:
1314
1315@example
1316(define-project-bindings 'unwriteable-directory
1317 '((nil . ((some-useful-setting . value)))))
1318
1319(set-directory-project "/usr/include/" 'unwriteable-directory)
1320@end example
1321
1322 Unsafe directory-local variables are handled in the same way as
1323unsafe file-local variables.
1324
1265@node Key Bindings 1325@node Key Bindings
1266@section Customizing Key Bindings 1326@section Customizing Key Bindings
1267@cindex key bindings 1327@cindex key bindings
diff --git a/etc/NEWS b/etc/NEWS
index 528337dc2ec..be0cfa26962 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -180,6 +180,9 @@ run processes remotely.
180** The new command `display-time-world' starts an updating time display 180** The new command `display-time-world' starts an updating time display
181using several time zones, in a buffer. 181using several time zones, in a buffer.
182 182
183** Directory-local variables are now found in .dir-settings.el. See
184also `set-directory-project' and `define-project-bindings'.
185
183** The new function `format-seconds' converts a number of seconds into a 186** The new function `format-seconds' converts a number of seconds into a
184readable string of days, hours, etc. 187readable string of days, hours, etc.
185 188
diff --git a/lisp/files.el b/lisp/files.el
index b2f3002d3dd..3afbb2a1d18 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -1973,6 +1973,8 @@ in that case, this function acts as if `enable-local-variables' were t."
1973 (let ((enable-local-variables (or (not find-file) enable-local-variables))) 1973 (let ((enable-local-variables (or (not find-file) enable-local-variables)))
1974 (report-errors "File mode specification error: %s" 1974 (report-errors "File mode specification error: %s"
1975 (set-auto-mode)) 1975 (set-auto-mode))
1976 (report-errors "Project local-variables error: %s"
1977 (hack-project-variables))
1976 (report-errors "File local-variables error: %s" 1978 (report-errors "File local-variables error: %s"
1977 (hack-local-variables))) 1979 (hack-local-variables)))
1978 ;; Turn font lock off and on, to make sure it takes account of 1980 ;; Turn font lock off and on, to make sure it takes account of
@@ -2623,11 +2625,13 @@ asking you for confirmation."
2623 2625
2624(put 'c-set-style 'safe-local-eval-function t) 2626(put 'c-set-style 'safe-local-eval-function t)
2625 2627
2626(defun hack-local-variables-confirm (all-vars unsafe-vars risky-vars) 2628(defun hack-local-variables-confirm (all-vars unsafe-vars risky-vars project)
2627 "Get confirmation before setting up local variable values. 2629 "Get confirmation before setting up local variable values.
2628ALL-VARS is the list of all variables to be set up. 2630ALL-VARS is the list of all variables to be set up.
2629UNSAFE-VARS is the list of those that aren't marked as safe or risky. 2631UNSAFE-VARS is the list of those that aren't marked as safe or risky.
2630RISKY-VARS is the list of those that are marked as risky." 2632RISKY-VARS is the list of those that are marked as risky.
2633PROJECT is a directory name if these settings come from directory-local
2634settings; nil otherwise."
2631 (if noninteractive 2635 (if noninteractive
2632 nil 2636 nil
2633 (let ((name (if buffer-file-name 2637 (let ((name (if buffer-file-name
@@ -2641,15 +2645,16 @@ RISKY-VARS is the list of those that are marked as risky."
2641 (set (make-local-variable 'cursor-type) nil) 2645 (set (make-local-variable 'cursor-type) nil)
2642 (erase-buffer) 2646 (erase-buffer)
2643 (if unsafe-vars 2647 (if unsafe-vars
2644 (insert "The local variables list in " name 2648 (insert "The local variables list in " (or project name)
2645 "\ncontains values that may not be safe (*)" 2649 "\ncontains values that may not be safe (*)"
2646 (if risky-vars 2650 (if risky-vars
2647 ", and variables that are risky (**)." 2651 ", and variables that are risky (**)."
2648 ".")) 2652 "."))
2649 (if risky-vars 2653 (if risky-vars
2650 (insert "The local variables list in " name 2654 (insert "The local variables list in " (or project name)
2651 "\ncontains variables that are risky (**).") 2655 "\ncontains variables that are risky (**).")
2652 (insert "A local variables list is specified in " name "."))) 2656 (insert "A local variables list is specified in "
2657 (or project name) ".")))
2653 (insert "\n\nDo you want to apply it? You can type 2658 (insert "\n\nDo you want to apply it? You can type
2654y -- to apply the local variables list. 2659y -- to apply the local variables list.
2655n -- to ignore the local variables list.") 2660n -- to ignore the local variables list.")
@@ -2771,6 +2776,50 @@ and VAL is the specified value."
2771 mode-specified 2776 mode-specified
2772 result)))) 2777 result))))
2773 2778
2779(defun hack-local-variables-apply (result project)
2780 "Apply an alist of local variable settings.
2781RESULT is the alist.
2782Will query the user when necessary."
2783 (dolist (ignored ignored-local-variables)
2784 (setq result (assq-delete-all ignored result)))
2785 (if (null enable-local-eval)
2786 (setq result (assq-delete-all 'eval result)))
2787 (when result
2788 (setq result (nreverse result))
2789 ;; Find those variables that we may want to save to
2790 ;; `safe-local-variable-values'.
2791 (let (risky-vars unsafe-vars)
2792 (dolist (elt result)
2793 (let ((var (car elt))
2794 (val (cdr elt)))
2795 ;; Don't query about the fake variables.
2796 (or (memq var '(mode unibyte coding))
2797 (and (eq var 'eval)
2798 (or (eq enable-local-eval t)
2799 (hack-one-local-variable-eval-safep
2800 (eval (quote val)))))
2801 (safe-local-variable-p var val)
2802 (and (risky-local-variable-p var val)
2803 (push elt risky-vars))
2804 (push elt unsafe-vars))))
2805 (if (eq enable-local-variables :safe)
2806 ;; If caller wants only the safe variables,
2807 ;; install only them.
2808 (dolist (elt result)
2809 (unless (or (member elt unsafe-vars)
2810 (member elt risky-vars))
2811 (hack-one-local-variable (car elt) (cdr elt))))
2812 ;; Query, except in the case where all are known safe
2813 ;; if the user wants no query in that case.
2814 (if (or (and (eq enable-local-variables t)
2815 (null unsafe-vars)
2816 (null risky-vars))
2817 (eq enable-local-variables :all)
2818 (hack-local-variables-confirm
2819 result unsafe-vars risky-vars project))
2820 (dolist (elt result)
2821 (hack-one-local-variable (car elt) (cdr elt))))))))
2822
2774(defun hack-local-variables (&optional mode-only) 2823(defun hack-local-variables (&optional mode-only)
2775 "Parse and put into effect this buffer's local variables spec. 2824 "Parse and put into effect this buffer's local variables spec.
2776If MODE-ONLY is non-nil, all we do is check whether the major mode 2825If MODE-ONLY is non-nil, all we do is check whether the major mode
@@ -2862,45 +2911,7 @@ is specified, returning t if it is specified."
2862 ;; variables (if MODE-ONLY is nil.) 2911 ;; variables (if MODE-ONLY is nil.)
2863 (if mode-only 2912 (if mode-only
2864 result 2913 result
2865 (dolist (ignored ignored-local-variables) 2914 (hack-local-variables-apply result nil)
2866 (setq result (assq-delete-all ignored result)))
2867 (if (null enable-local-eval)
2868 (setq result (assq-delete-all 'eval result)))
2869 (when result
2870 (setq result (nreverse result))
2871 ;; Find those variables that we may want to save to
2872 ;; `safe-local-variable-values'.
2873 (let (risky-vars unsafe-vars)
2874 (dolist (elt result)
2875 (let ((var (car elt))
2876 (val (cdr elt)))
2877 ;; Don't query about the fake variables.
2878 (or (memq var '(mode unibyte coding))
2879 (and (eq var 'eval)
2880 (or (eq enable-local-eval t)
2881 (hack-one-local-variable-eval-safep
2882 (eval (quote val)))))
2883 (safe-local-variable-p var val)
2884 (and (risky-local-variable-p var val)
2885 (push elt risky-vars))
2886 (push elt unsafe-vars))))
2887 (if (eq enable-local-variables :safe)
2888 ;; If caller wants only the safe variables,
2889 ;; install only them.
2890 (dolist (elt result)
2891 (unless (or (member elt unsafe-vars)
2892 (member elt risky-vars))
2893 (hack-one-local-variable (car elt) (cdr elt))))
2894 ;; Query, except in the case where all are known safe
2895 ;; if the user wants no quuery in that case.
2896 (if (or (and (eq enable-local-variables t)
2897 (null unsafe-vars)
2898 (null risky-vars))
2899 (eq enable-local-variables :all)
2900 (hack-local-variables-confirm
2901 result unsafe-vars risky-vars))
2902 (dolist (elt result)
2903 (hack-one-local-variable (car elt) (cdr elt)))))))
2904 (run-hooks 'hack-local-variables-hook))))) 2915 (run-hooks 'hack-local-variables-hook)))))
2905 2916
2906(defun safe-local-variable-p (sym val) 2917(defun safe-local-variable-p (sym val)
@@ -3004,6 +3015,167 @@ already the major mode."
3004 (if (stringp val) 3015 (if (stringp val)
3005 (set-text-properties 0 (length val) nil val)) 3016 (set-text-properties 0 (length val) nil val))
3006 (set (make-local-variable var) val)))) 3017 (set (make-local-variable var) val))))
3018
3019;;; Handling directory local variables, aka project settings.
3020
3021(defvar project-class-alist '()
3022 "Alist mapping project class names (symbols) to project variable lists.")
3023
3024(defvar project-directory-alist '()
3025 "Alist mapping project directory roots to project classes.")
3026
3027(defsubst project-get-alist (class)
3028 "Return the project variable list for project CLASS."
3029 (cdr (assq class project-class-alist)))
3030
3031(defun project-collect-bindings-from-alist (mode-alist settings)
3032 "Collect local variable settings from MODE-ALIST.
3033SETTINGS is the initial list of bindings.
3034Returns the new list."
3035 (dolist (pair mode-alist settings)
3036 (let* ((variable (car pair))
3037 (value (cdr pair))
3038 (slot (assq variable settings)))
3039 (if slot
3040 (setcdr slot value)
3041 ;; Need a new cons in case we setcdr later.
3042 (push (cons variable value) settings)))))
3043
3044(defun project-collect-binding-list (binding-list root settings)
3045 "Collect entries from BINDING-LIST into SETTINGS.
3046ROOT is the root directory of the project.
3047Return the new settings list."
3048 (let* ((file-name (buffer-file-name))
3049 (sub-file-name (if file-name
3050 (substring file-name (length root)))))
3051 (dolist (entry binding-list settings)
3052 (let ((key (car entry)))
3053 (cond
3054 ((stringp key)
3055 ;; Don't include this in the previous condition, because we
3056 ;; want to filter all strings before the next condition.
3057 (when (and sub-file-name
3058 (>= (length sub-file-name) (length key))
3059 (string= key (substring sub-file-name 0 (length key))))
3060 (setq settings (project-collect-binding-list (cdr entry)
3061 root settings))))
3062 ((or (not key)
3063 (derived-mode-p key))
3064 (setq settings (project-collect-bindings-from-alist (cdr entry)
3065 settings))))))))
3066
3067(defun set-directory-project (directory class)
3068 "Declare that the project rooted at DIRECTORY is an instance of CLASS.
3069DIRECTORY is the name of a directory, a string.
3070CLASS is the name of a project class, a symbol.
3071
3072When a file beneath DIRECTORY is visited, the mode-specific
3073settings from CLASS will be applied to the buffer. The settings
3074for a class are defined using `define-project-bindings'."
3075 (setq directory (file-name-as-directory (expand-file-name directory)))
3076 (unless (assq class project-class-alist)
3077 (error "No such project class `%s'" (symbol-name class)))
3078 (push (cons directory class) project-directory-alist))
3079
3080(defun define-project-bindings (class list)
3081 "Map the project type CLASS to a list of variable settings.
3082CLASS is the project class, a symbol.
3083LIST is a list that declares variable settings for the class.
3084An element in LIST is either of the form:
3085 (MAJOR-MODE . ALIST)
3086or
3087 (DIRECTORY . LIST)
3088
3089In the first form, MAJOR-MODE is a symbol, and ALIST is an alist
3090whose elements are of the form (VARIABLE . VALUE).
3091
3092In the second form, DIRECTORY is a directory name (a string), and
3093LIST is a list of the form accepted by the function.
3094
3095When a file is visited, the file's class is found. A directory
3096may be assigned a class using `set-directory-project'. Then
3097variables are set in the file's buffer according to the class'
3098LIST. The list is processed in order.
3099
3100* If the element is of the form (MAJOR-MODE . ALIST), and the
3101 buffer's major mode is derived from MAJOR-MODE (as determined
3102 by `derived-mode-p'), then all the settings in ALIST are
3103 applied. A MAJOR-MODE of nil may be used to match any buffer.
3104 `make-local-variable' is called for each variable before it is
3105 set.
3106
3107* If the element is of the form (DIRECTORY . LIST), and DIRECTORY
3108 is an initial substring of the file's directory, then LIST is
3109 applied by recursively following these rules."
3110 (let ((elt (assq class project-class-alist)))
3111 (if elt
3112 (setcdr elt list)
3113 (push (cons class list) project-class-alist))))
3114
3115(defun project-find-settings-file (file)
3116 "Find the settings file for FILE.
3117This searches upward in the directory tree.
3118If a settings file is found, the file name is returned.
3119If the file is in a registered project, a cons from
3120`project-directory-alist' is returned.
3121Otherwise this returns nil."
3122 (let ((dir (file-name-directory file))
3123 (result nil))
3124 (while (and (not (string= dir "/"))
3125 (not result))
3126 (cond
3127 ((setq result (assoc dir project-directory-alist))
3128 ;; Nothing else.
3129 nil)
3130 ((file-exists-p (concat dir ".dir-settings.el"))
3131 (setq result (concat dir ".dir-settings.el")))
3132 (t
3133 (setq dir (file-name-directory (directory-file-name dir))))))
3134 result))
3135
3136(defun project-define-from-project-file (settings-file)
3137 "Load a settings file and register a new project class and instance.
3138SETTINGS-FILE is the name of the file holding the settings to apply.
3139The new class name is the same as the directory in which SETTINGS-FILE
3140is found. Returns the new class name."
3141 (with-temp-buffer
3142 ;; We should probably store the modtime of SETTINGS-FILE and then
3143 ;; reload it whenever it changes.
3144 (insert-file-contents settings-file)
3145 (let* ((dir-name (file-name-directory settings-file))
3146 (class-name (intern dir-name))
3147 (list (read (current-buffer))))
3148 (define-project-bindings class-name list)
3149 (set-directory-project dir-name class-name)
3150 class-name)))
3151
3152(defun hack-project-variables ()
3153 "Set local variables in a buffer based on project settings."
3154 (when (and (buffer-file-name) (not (file-remote-p (buffer-file-name))))
3155 ;; Find the settings file.
3156 (let ((settings (project-find-settings-file (buffer-file-name)))
3157 (class nil)
3158 (root-dir nil))
3159 (cond
3160 ((stringp settings)
3161 (setq root-dir (file-name-directory (buffer-file-name)))
3162 (setq class (project-define-from-project-file settings)))
3163 ((consp settings)
3164 (setq root-dir (car settings))
3165 (setq class (cdr settings))))
3166 (when class
3167 (let ((bindings
3168 (project-collect-binding-list (project-get-alist class)
3169 root-dir nil)))
3170 (when bindings
3171 (hack-local-variables-apply bindings root-dir)
3172 ;; Special case C and derived modes. Note that CC-based
3173 ;; modes don't work with derived-mode-p. In general I
3174 ;; think modes could use an auxiliary method which is
3175 ;; called after local variables are hacked.
3176 (and (boundp 'c-buffer-is-cc-mode)
3177 c-buffer-is-cc-mode
3178 (c-postprocess-file-styles))))))))
3007 3179
3008 3180
3009(defcustom change-major-mode-with-file-name t 3181(defcustom change-major-mode-with-file-name t