all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Alan Mackenzie <acm@muc.de>
To: Stefan Monnier <monnier@iro.umontreal.ca>, Glenn Morris <rgm@gnu.org>
Cc: emacs-devel@gnu.org
Subject: Re: bug#347: C mode asks twice about local variables
Date: Sun, 15 Jun 2008 22:04:12 +0000	[thread overview]
Message-ID: <20080615220412.GF2448@muc.de> (raw)
In-Reply-To: <jwvy75epfv1.fsf-monnier+emacsbugreports@gnu.org>

'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  <acm@muc.de>

	* 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).




  parent reply	other threads:[~2008-06-15 22:04 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <871w1aywbx.fsf@stupidchicken.com>
2008-05-31 22:51 ` bug#343: C mode asks twice about local variables Glenn Morris
2008-06-01 17:21   ` Alan Mackenzie
2008-06-03  6:40     ` bug#347: " Stefan Monnier
2008-06-09 14:36       ` Alan Mackenzie
2008-06-09 15:18         ` Stefan Monnier
2008-06-09 19:07           ` Alan Mackenzie
2008-06-11 14:41             ` Stefan Monnier
2008-06-11 16:37               ` Glenn Morris
2008-06-11 17:51                 ` Stefan Monnier
2008-06-15 22:04               ` Alan Mackenzie [this message]
2008-07-26 10:44                 ` Alan Mackenzie
2008-07-26 15:05                   ` Chong Yidong
2008-07-26 15:58                     ` Chong Yidong
2008-07-28 18:11                     ` Johan Bockgård
2008-07-31  1:37                   ` Chong Yidong
2008-07-31 14:20     ` bug#347: marked as done (C mode asks twice about local variables) Emacs bug Tracking System
2008-07-31 14:20   ` bug#343: " Emacs bug Tracking System

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20080615220412.GF2448@muc.de \
    --to=acm@muc.de \
    --cc=emacs-devel@gnu.org \
    --cc=monnier@iro.umontreal.ca \
    --cc=rgm@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.