aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorMichael Olson2008-05-19 22:36:09 +0000
committerMichael Olson2008-05-19 22:36:09 +0000
commit1b21ee065d77baee6da0c46a207bfb44d7cc0a95 (patch)
treeac173ee06682f6fe2d09403c2be5d246b9d26558 /lisp
parentad97b375e8189e1826d562898ea78e4f3bb94bda (diff)
downloademacs-1b21ee065d77baee6da0c46a207bfb44d7cc0a95.tar.gz
emacs-1b21ee065d77baee6da0c46a207bfb44d7cc0a95.zip
Implement Project-local variables.
Diffstat (limited to 'lisp')
-rw-r--r--lisp/files.el260
1 files changed, 216 insertions, 44 deletions
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