From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Alan Mackenzie Newsgroups: gmane.emacs.devel Subject: Re: bug#347: C mode asks twice about local variables Date: Sun, 15 Jun 2008 22:04:12 +0000 Message-ID: <20080615220412.GF2448@muc.de> References: <20080601172143.GA5899@muc.de> <20080609143651.GA6098@muc.de> <20080609190750.GC6098@muc.de> NNTP-Posting-Host: lo.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: ger.gmane.org 1213566099 5253 80.91.229.12 (15 Jun 2008 21:41:39 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Sun, 15 Jun 2008 21:41:39 +0000 (UTC) Cc: emacs-devel@gnu.org To: Stefan Monnier , Glenn Morris Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Sun Jun 15 23:42:22 2008 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([199.232.76.165]) by lo.gmane.org with esmtp (Exim 4.50) id 1K7zzQ-0000TO-QW for ged-emacs-devel@m.gmane.org; Sun, 15 Jun 2008 23:42:21 +0200 Original-Received: from localhost ([127.0.0.1]:47664 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1K7zyc-0005Rv-Kl for ged-emacs-devel@m.gmane.org; Sun, 15 Jun 2008 17:41:30 -0400 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1K7zyZ-0005Rq-GM for emacs-devel@gnu.org; Sun, 15 Jun 2008 17:41:27 -0400 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1K7zyX-0005Re-SP for emacs-devel@gnu.org; Sun, 15 Jun 2008 17:41:26 -0400 Original-Received: from [199.232.76.173] (port=35946 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1K7zyX-0005Rb-N8 for emacs-devel@gnu.org; Sun, 15 Jun 2008 17:41:25 -0400 Original-Received: from colin.muc.de ([193.149.48.1]:4316 helo=mail.muc.de) by monty-python.gnu.org with esmtps (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1K7zyV-0002KC-Ib for emacs-devel@gnu.org; Sun, 15 Jun 2008 17:41:25 -0400 Original-Received: (qmail 64392 invoked by uid 3782); 15 Jun 2008 21:41:18 -0000 Original-Received: from acm.muc.de (pD9E50FCF.dip.t-dialin.net [217.229.15.207]) by colin2.muc.de (tmda-ofmipd) with ESMTP; Sun, 15 Jun 2008 23:41:16 +0200 Original-Received: (qmail 9735 invoked by uid 1000); 15 Jun 2008 22:04:12 -0000 Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.9i X-Delivery-Agent: TMDA/1.1.5 (Fettercairn) X-Primary-Address: acm@muc.de X-detected-kernel: by monty-python.gnu.org: FreeBSD 4.6-4.9 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:99272 Archived-At: '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 * 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).