[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: bug#347: C mode asks twice about local variables
From: |
Alan Mackenzie |
Subject: |
Re: bug#347: C mode asks twice about local variables |
Date: |
Sun, 15 Jun 2008 22:04:12 +0000 |
User-agent: |
Mutt/1.5.9i |
'Evening, Stefan and Glenn!
On Wed, Jun 11, 2008 at 10:41:38AM -0400, Stefan Monnier wrote:
> > I don't know off-hand what the significance of buffer locality is in
> > CC Mode styles.
> Could you ask the CC mode maintainer, maybe?
He was as confused about it as I was.
> > What I had in mind was using an (if (boundp
> > 'before-hack-local-variables) ...) to separate out new strategy from
> > old.
[ .... ]
> > Oh, and the hook would need documenting in the Elisp manual, but I
> > can manage that.
> > Hey, this is so easy and obviously the right thing. Let's do it!
> It's going in the right direction but I'm still not completely
> satisfied. How 'bout something like the following:
> Some variables can be flagged as being special, in that when they apper
> in the file-local list of settings, they "get set" by calling
> a function. E.g. `mode' is globally special and "setting it" will
> actually call the corresponding mode.
> Then `c-mode' can define `c-file-style' and `c-file-offsets' as being
> special so that setting them actually calls a function of yours
> (probably a function that could also be used for the :set in defcustom,
> BTW).
> Of course, the interesting bit is that hack-local-variables will be
> careful to reorder the file-local settings such that special variables
> are set first.
Why, in general, should "special" variables always be set first? I
can't feel any enthusiasm for this approach.
To verify my feelings about the `before-hack-local-variables-hook'
approach, I have done a trial implementation of it. It was a little
more complicated that I'd expected - I had to refactor a bit, replacing
`hack-local-variables-apply' with `hack-local-variables-filter' (which
removes unwanted variables from the hack-list rather than setting the
rest) and moving the setting of the hack-variables into h-l-v itself.
The change to cc-mode.el was straightforward.
See what you think. Here is the trial patch to the code:
2008-06-15 Alan Mackenzie <address@hidden>
* progmodes/cc-mode.el (c-before-hack-hook): New function
(Top Level): Install c-before-hack-hook on
before-hack-local-variables-hook, rather than
c-postprocess-file-styles on hack-local-variables-hook.
* files.el (hack-local-variables-alist): New variable.
(before-hack-local-variables-hook): New hook.
(hack-local-variables-filter): Refactored version of
Hack-local-variables-apply.
(hack-local-variables): Call `before-hack-local-variables-hook'.
Index: cc-mode.el
===================================================================
RCS file: /cvsroot/emacs/emacs/lisp/progmodes/cc-mode.el,v
retrieving revision 1.76
diff -c -r1.76 cc-mode.el
*** cc-mode.el 26 May 2008 06:57:40 -0000 1.76
--- cc-mode.el 15 Jun 2008 21:06:32 -0000
***************
*** 656,661 ****
--- 656,681 ----
(and (cdr rfn)
(setq require-final-newline mode-require-final-newline)))))
+ (defun c-before-hack-hook ()
+ "Set the CC Mode style and \"offsets\" when in the buffer's local variables.
+ They are set only when, respectively, the pseudo variables
+ `c-file-style' and `c-file-offsets' are present in the list.
+
+ This function is called from the hook `before-hack-local-variables-hook'."
+ (when c-buffer-is-cc-mode
+ (let ((stile (cdr (assq 'c-file-style hack-local-variables-alist)))
+ (offsets (cdr (assq 'c-file-offsets hack-local-variables-alist))))
+ (when stile
+ (or (stringp stile) (error "c-file-style is not a string"))
+ (c-set-style stile))
+ (when offsets
+ (mapc
+ (lambda (langentry)
+ (let ((langelem (car langentry))
+ (offset (cdr langentry)))
+ (c-set-offset langelem offset)))
+ offsets)))))
+
(defun c-remove-any-local-eval-or-mode-variables ()
;; If the buffer specifies `mode' or `eval' in its File Local Variable list
;; or on the first line, remove all occurrences. See
***************
*** 747,753 ****
(hack-local-variables))
nil))))
! (add-hook 'hack-local-variables-hook 'c-postprocess-file-styles)
(defmacro c-run-mode-hooks (&rest hooks)
;; Emacs 21.1 has introduced a system with delayed mode hooks that
--- 767,775 ----
(hack-local-variables))
nil))))
! (if (boundp 'before-hack-local-variables-hook)
! (add-hook 'before-hack-local-variables-hook 'c-before-hack-hook)
! (add-hook 'hack-local-variables-hook 'c-postprocess-file-styles))
(defmacro c-run-mode-hooks (&rest hooks)
;; Emacs 21.1 has introduced a system with delayed mode hooks that
Index: files.el
===================================================================
RCS file: /cvsroot/emacs/emacs/lisp/files.el,v
retrieving revision 1.985
diff -c -r1.985 files.el
*** files.el 11 Jun 2008 01:47:47 -0000 1.985
--- files.el 15 Jun 2008 20:46:17 -0000
***************
*** 2514,2519 ****
--- 2514,2539 ----
'(ignored-local-variables safe-local-variable-values)
"Variables to be ignored in a file's local variable spec.")
+ (defvar hack-local-variables-alist nil
+ "Alist of (VAR . VALUE) pairs read from a buffer's local variables.
+ VAR, a symbol, is a variable to be set, and VALUE (unevaluated) is
+ the value it will get set to.
+
+ This alist contains the settings from both the \"-*-\" line at
+ the top of the buffer and the \"Local Variables\:\" section near
+ the bottom of the buffer. The settings are in the same order as
+ in the buffer.")
+
+ (defvar before-hack-local-variables-hook nil
+ "Normal hook run before setting a file's local variables.
+ It is called after the checks for unsafe and risky variables are
+ done. It is called only when there is at least one local
+ variable to set.
+
+ The details of the local variables are in the variable
+ `hack-local-variables-alist'; a hook function may change the
+ contents of this alist.")
+
(defvar hack-local-variables-hook nil
"Normal hook run after processing a file's local variables specs.
Major modes can use this to examine user-specified local variables
***************
*** 2777,2798 ****
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."
(dolist (ignored ignored-local-variables)
! (setq result (assq-delete-all ignored result)))
(if (null enable-local-eval)
! (setq result (assq-delete-all 'eval result)))
! (when result
! (setq result (nreverse result))
;; Find those variables that we may want to save to
;; `safe-local-variable-values'.
(let (risky-vars unsafe-vars)
! (dolist (elt result)
(let ((var (car elt))
(val (cdr elt)))
;; Don't query about the fake variables.
(or (memq var '(mode unibyte coding))
(and (eq var 'eval)
--- 2797,2830 ----
mode-specified
result))))
! (defun hack-local-variables-filter (variables project)
! "Remove risky \(etc.) local variables from VARIABLES.
! These are determined from the options `enable-local-variables',
! `enable-local-eval', `ignored-local-variables' and possibly the
! result of querying the user.
!
! VARIABLES is an alist, each element of which has the form (VAR
! . VALUE), VAR being a variable to set (a symbol), VALUE being its
! \(unevaluted) value. This format is the same as
! `hack-local-variables-alist''s. This function might modify
! VARIABLES's list structure.
!
! PROJECT is .... ?????
!
! The function's result is VARIABLES with all rejected variables
! removed. This may well be nil."
(dolist (ignored ignored-local-variables)
! (setq variables (assq-delete-all ignored variables)))
(if (null enable-local-eval)
! (setq variables (assq-delete-all 'eval variables)))
! (when variables
;; Find those variables that we may want to save to
;; `safe-local-variable-values'.
(let (risky-vars unsafe-vars)
! (dolist (elt variables)
(let ((var (car elt))
(val (cdr elt)))
+ ;; Scan the variables, collecting risky and unsafe ones.
;; Don't query about the fake variables.
(or (memq var '(mode unibyte coding))
(and (eq var 'eval)
***************
*** 2803,2815 ****
(and (risky-local-variable-p var val)
(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)
! (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.
(if (or (and (eq enable-local-variables t)
--- 2835,2848 ----
(and (risky-local-variable-p var val)
(push elt risky-vars))
(push elt unsafe-vars))))
+
(if (eq enable-local-variables :safe)
;; If caller wants only the safe variables,
! ;; expunge the list of the rest.
! (dolist (elt variables)
! (if (or (member elt unsafe-vars)
! (member elt risky-vars))
! (setq variables (assq-delete-all (car elt) variables))))
;; Query, except in the case where all are known safe
;; if the user wants no query in that case.
(if (or (and (eq enable-local-variables t)
***************
*** 2817,2825 ****
(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))))))))
(defun hack-local-variables (&optional mode-only)
"Parse and put into effect this buffer's local variables spec.
--- 2850,2858 ----
(null risky-vars))
(eq enable-local-variables :all)
(hack-local-variables-confirm
! variables unsafe-vars risky-vars project))
! variables
! )))))
(defun hack-local-variables (&optional mode-only)
"Parse and put into effect this buffer's local variables spec.
***************
*** 2827,2835 ****
is specified, returning t if it is specified."
(let ((enable-local-variables
(and local-enable-local-variables enable-local-variables))
! result)
(when (or mode-only enable-local-variables)
! (setq result (hack-local-variables-prop-line mode-only))
;; Look for "Local variables:" line in last page.
(save-excursion
(goto-char (point-max))
--- 2860,2868 ----
is specified, returning t if it is specified."
(let ((enable-local-variables
(and local-enable-local-variables enable-local-variables))
! result hack-local-variables-alist)
(when (or mode-only enable-local-variables)
! (setq hack-local-variables-alist (hack-local-variables-prop-line
mode-only))
;; Look for "Local variables:" line in last page.
(save-excursion
(goto-char (point-max))
***************
*** 2906,2921 ****
(push (cons (if (eq var 'eval)
'eval
(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)))))
(defun safe-local-variable-p (sym val)
--- 2939,2959 ----
(push (cons (if (eq var 'eval)
'eval
(indirect-variable var))
! val) hack-local-variables-alist)
(error nil)))))
(forward-line 1)))))))
+ (setq hack-local-variables-alist (nreverse hack-local-variables-alist))
;; 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
! (setq hack-local-variables-alist
! (hack-local-variables-filter hack-local-variables-alist nil))
! (run-hooks 'before-hack-local-variables-hook)
! (dolist (elt hack-local-variables-alist)
! (hack-one-local-variable (car elt) (cdr elt)))
(run-hooks 'hack-local-variables-hook)))))
(defun safe-local-variable-p (sym val)
> Stefan
--
Alan Mackenzie (Nuremberg, Germany).