changeset 97137:c33982d8abdc

(normal-mode): Move call to hack-project-variables into hack-local-variables. (file-local-variables-alist, before-hack-local-variables-hook): New vars. (ignored-local-variables): Add file-local-variables-alist. (hack-local-variables-filter): Renamed from hack-local-variables-apply. Add the result to file-local-variables-alist, without applying them. (hack-local-variables): Set file-local-variables-alist to nil. Call hack-project-variables and before-hack-local-variables-hook. Apply variables here, instead of hack-local-variables-apply. Based on a patch by Alan Mackenzie.
author Chong Yidong <cyd@stupidchicken.com>
date Thu, 31 Jul 2008 01:22:54 +0000
parents 1aea5b713a31
children cc10cad95d85
files lisp/files.el
diffstat 1 files changed, 70 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/files.el	Thu Jul 31 01:22:29 2008 +0000
+++ b/lisp/files.el	Thu Jul 31 01:22:54 2008 +0000
@@ -1984,8 +1984,6 @@
   (let ((enable-local-variables (or (not find-file) enable-local-variables)))
     (report-errors "File mode specification error: %s"
       (set-auto-mode))
-    (report-errors "Project local-variables error: %s"
-      (hack-project-variables))
     (report-errors "File local-variables error: %s"
       (hack-local-variables)))
   ;; Turn font lock off and on, to make sure it takes account of
@@ -2521,7 +2519,8 @@
 ;;; Handling file local variables
 
 (defvar ignored-local-variables
-  '(ignored-local-variables safe-local-variable-values)
+  '(ignored-local-variables safe-local-variable-values
+    file-local-variables-alist)
   "Variables to be ignored in a file's local variable spec.")
 
 (defvar hack-local-variables-hook nil
@@ -2636,13 +2635,31 @@
 
 (put 'c-set-style 'safe-local-eval-function t)
 
+(defvar file-local-variables-alist nil
+  "Alist of file-local variable settings in the current buffer.
+Each element in this list has the form (VAR . VALUE), where VAR
+is a file-local variable (a symbol) and VALUE is the value
+specified.  The actual value in the buffer may differ from VALUE,
+if it is changed by the major or minor modes, or by the user.")
+(make-variable-buffer-local 'file-local-variables-alist)
+
+(defvar before-hack-local-variables-hook nil
+  "Normal hook run before setting file-local variables.
+It is called after checking for unsafe/risky variables and
+setting `file-local-variables-alist', and before applying the
+variables stored in `file-local-variables-alist'.  A hook
+function is allowed to change the contents of this alist.
+
+This hook is called only if there is at least one file-local
+variable to set.")
+
 (defun hack-local-variables-confirm (all-vars unsafe-vars risky-vars project)
   "Get confirmation before setting up local variable values.
 ALL-VARS is the list of all variables to be set up.
 UNSAFE-VARS is the list of those that aren't marked as safe or risky.
 RISKY-VARS is the list of those that are marked as risky.
 PROJECT is a directory name if these settings come from directory-local
-settings; nil otherwise."
+settings, or nil otherwise."
   (if noninteractive
       nil
     (let ((name (if buffer-file-name
@@ -2787,20 +2804,27 @@
 	  mode-specified
 	result))))
 
-(defun hack-local-variables-apply (result project)
-  "Apply an alist of local variable settings.
-RESULT is the alist.
-Will query the user when necessary."
+(defun hack-local-variables-filter (variables project)
+  "Filter local variable settings, querying the user if necessary.
+VARIABLES is the alist of variable-value settings.  This alist is
+ filtered based on the values of `ignored-local-variables',
+ `enable-local-eval', `enable-local-variables', and (if necessary)
+ user interaction.  The results are added to
+ `file-local-variables-alist', without applying them.
+PROJECT is a directory name if these settings come from
+ directory-local settings, or nil otherwise."
+  ;; Strip any variables that are in `ignored-local-variables'.
   (dolist (ignored ignored-local-variables)
-    (setq result (assq-delete-all ignored result)))
+    (setq variables (assq-delete-all ignored variables)))
+  ;; If `enable-local-eval' is nil, strip eval "variables".
   (if (null enable-local-eval)
-      (setq result (assq-delete-all 'eval result)))
-  (when result
-    (setq result (nreverse result))
+      (setq variables (assq-delete-all 'eval variables)))
+  (setq variables (nreverse variables))
+  (when variables
     ;; Find those variables that we may want to save to
     ;; `safe-local-variable-values'.
     (let (risky-vars unsafe-vars)
-      (dolist (elt result)
+      (dolist (elt variables)
 	(let ((var (car elt))
 	      (val (cdr elt)))
 	  ;; Don't query about the fake variables.
@@ -2814,22 +2838,21 @@
 		   (push elt risky-vars))
 	      (push elt unsafe-vars))))
       (if (eq enable-local-variables :safe)
-	  ;; If caller wants only the safe variables,
-	  ;; install only them.
-	  (dolist (elt result)
+	  ;; If caller wants only safe variables, store only these.
+	  (dolist (elt variables)
 	    (unless (or (member elt unsafe-vars)
 			(member elt risky-vars))
-	      (hack-one-local-variable (car elt) (cdr elt))))
-	;; Query, except in the case where all are known safe
-	;; if the user wants no query in that case.
+	      (push elt file-local-variables-alist)))
+	;; Query, unless all are known safe or the user wants no
+	;; querying.
 	(if (or (and (eq enable-local-variables t)
 		     (null unsafe-vars)
 		     (null risky-vars))
 		(eq enable-local-variables :all)
 		(hack-local-variables-confirm
-		 result unsafe-vars risky-vars project))
-	    (dolist (elt result)
-	      (hack-one-local-variable (car elt) (cdr elt))))))))
+		 variables unsafe-vars risky-vars project))
+	    (dolist (elt variables)
+	      (push elt file-local-variables-alist)))))))
 
 (defun hack-local-variables (&optional mode-only)
   "Parse and put into effect this buffer's local variables spec.
@@ -2838,6 +2861,10 @@
   (let ((enable-local-variables
 	 (and local-enable-local-variables enable-local-variables))
 	result)
+    (unless mode-only
+      (setq file-local-variables-alist nil)
+      (report-errors "Project local-variables error: %s"
+	(hack-project-variables)))
     (when (or mode-only enable-local-variables)
       (setq result (hack-local-variables-prop-line mode-only))
       ;; Look for "Local variables:" line in last page.
@@ -2918,15 +2945,20 @@
 					  (indirect-variable var))
 					val) result)
 			  (error nil)))))
-		  (forward-line 1)))))))
-
-      ;; We've read all the local variables.  Now, return whether the
-      ;; mode is specified (if MODE-ONLY is non-nil), or set the
-      ;; variables (if MODE-ONLY is nil.)
-      (if mode-only
-	  result
-	(hack-local-variables-apply result nil)
-	(run-hooks 'hack-local-variables-hook)))))
+		  (forward-line 1))))))))
+    ;; Now we've read all the local variables.
+    ;; If MODE-ONLY is non-nil, return whether the mode was specified.
+    (cond (mode-only result)
+	  ;; Otherwise, set the variables.
+	  (enable-local-variables
+	   (hack-local-variables-filter result nil)
+	   (when file-local-variables-alist
+	     (setq file-local-variables-alist
+		   (nreverse file-local-variables-alist))
+	     (run-hooks 'before-hack-local-variables-hook)
+	     (dolist (elt file-local-variables-alist)
+	       (hack-one-local-variable (car elt) (cdr elt))))
+	   (run-hooks 'hack-local-variables-hook)))))
 
 (defun safe-local-variable-p (sym val)
   "Non-nil if SYM is safe as a file-local variable with value VAL.
@@ -3168,8 +3200,12 @@
 (declare-function c-postprocess-file-styles "cc-mode" ())
 
 (defun hack-project-variables ()
-  "Set local variables in a buffer based on project settings."
-  (when (and (buffer-file-name) (not (file-remote-p (buffer-file-name))))
+  "Read local variables for the current buffer based on project settings.
+Store the project variables in `file-local-variables-alist',
+without applying them."
+  (when (and enable-local-variables
+	     (buffer-file-name)
+	     (not (file-remote-p (buffer-file-name))))
     ;; Find the settings file.
     (let ((settings (project-find-settings-file (buffer-file-name)))
 	  (class nil)
@@ -3186,14 +3222,7 @@
 	       (project-collect-binding-list (project-get-alist class)
 					     root-dir nil)))
 	  (when bindings
-	    (hack-local-variables-apply bindings root-dir)
-	    ;; Special case C and derived modes.  Note that CC-based
-	    ;; modes don't work with derived-mode-p.  In general I
-	    ;; think modes could use an auxiliary method which is
-	    ;; called after local variables are hacked.
-	    (and (boundp 'c-buffer-is-cc-mode)
-		 c-buffer-is-cc-mode
-		 (c-postprocess-file-styles))))))))
+	    (hack-local-variables-filter bindings root-dir)))))))
 
 
 (defcustom change-major-mode-with-file-name t