From 7b447e9bda80e5de478922771a62c2b3a8f9b2aa Mon Sep 17 00:00:00 2001 From: Glenn Morris Date: Fri, 20 Jan 2012 16:41:05 -0800 Subject: [PATCH] File-local variable fixes. * lisp/files.el (local-enable-local-variables): Doc fix. (inhibit-local-variables-regexps): Rename from inhibit-first-line-modes-regexps. Keep old name as obsolete alias. Doc fix. Add some extensions from auto-coding-alist. (inhibit-local-variables-suffixes): Rename from inhibit-first-line-modes-suffixes. Doc fix. (inhibit-local-variables-p): New function, extracted from set-auto-mode-1. (set-auto-mode): Doc fix. Respect inhibit-local-variables-regexps. (set-auto-mode-1): Doc fix. Use inhibit-local-variables-p. (hack-local-variables): Doc fix. Make the mode-only case respect enable-local-variables and friends. Respect inhibit-local-variables-regexps for file-locals, but not for directory-locals. (set-visited-file-name): Take account of inhibit-local-variables-regexps. Whether it applies may change as the file name is changed. * lisp/jka-cmpr-hook.el (jka-compr-install): * lisp/jka-compr.el (jka-compr-uninstall): Update for inhibit-first-line-modes-suffixes name change. * etc/NEWS: Mention this change. Fixes: debbugs:10506 --- etc/NEWS | 8 ++ lisp/ChangeLog | 23 ++++ lisp/files.el | 306 ++++++++++++++++++++++++++---------------- lisp/jka-cmpr-hook.el | 12 +- lisp/jka-compr.el | 9 +- 5 files changed, 234 insertions(+), 124 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 11537363ef..743e3ce2e7 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -369,6 +369,14 @@ turn on `whitespace-mode' for *vc-diff* buffers. Modes should call *** Using "mode: MINOR-MODE" to enable a minor mode is deprecated. Instead, use "eval: (minor-mode 1)". +FIXME: inhibit-first-line-modes-regexps was not mentioned in lispref, +but this probably should be. +*** The variable `inhibit-first-line-modes-regexps' has been renamed +to `inhibit-local-variables-regexps'. As the name suggests, it now +applies to ALL file local variables, not just -*- mode ones. +The associated `inhibit-first-line-modes-suffixes' has been renamed +in the corresponding way. + +++ ** The variable `focus-follows-mouse' now always defaults to nil. diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 71211ca9af..248de3429f 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,26 @@ +2012-01-21 Glenn Morris + + * files.el (local-enable-local-variables): Doc fix. + (inhibit-local-variables-regexps): Rename from + inhibit-first-line-modes-regexps. Keep old name as obsolete alias. + Doc fix. Add some extensions from auto-coding-alist. + (inhibit-local-variables-suffixes): + Rename from inhibit-first-line-modes-suffixes. Doc fix. + (inhibit-local-variables-p): + New function, extracted from set-auto-mode-1. + (set-auto-mode): Doc fix. Respect inhibit-local-variables-regexps. + (set-auto-mode-1): Doc fix. Use inhibit-local-variables-p. + (hack-local-variables): Doc fix. Make the mode-only case + respect enable-local-variables and friends. + Respect inhibit-local-variables-regexps for file-locals, but + not for directory-locals. + (set-visited-file-name): + Take account of inhibit-local-variables-regexps. + Whether it applies may change as the file name is changed. + * jka-cmpr-hook.el (jka-compr-install): + * jka-compr.el (jka-compr-uninstall): + Update for inhibit-first-line-modes-suffixes name change. + 2012-01-20 Martin Rudalics * help-macro.el (make-help-screen): Temporarily restore original diff --git a/lisp/files.el b/lisp/files.el index 6056a70d4a..7a72775ac3 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -510,14 +510,36 @@ and ignores this variable." (other :tag "Query" other)) :group 'find-file) +;; This is an odd variable IMO. +;; You might wonder why it is needed, when we could just do: +;; (set (make-local-variable 'enable-local-variables) nil) +;; These two are not precisely the same. +;; Setting this variable does not cause -*- mode settings to be +;; ignored, whereas setting enable-local-variables does. +;; Only three places in Emacs use this variable: tar and arc modes, +;; and rmail. The first two don't need it. They already use +;; inhibit-local-variables-regexps, which is probably enough, and +;; could also just set enable-local-variables locally to nil. +;; Them setting it has the side-effect that dir-locals cannot apply to +;; eg tar files (?). FIXME Is this appropriate? +;; AFAICS, rmail is the only thing that needs this, and the only +;; reason it uses it is for BABYL files (which are obsolete). +;; These contain "-*- rmail -*-" in the first line, which rmail wants +;; to respect, so that find-file on a BABYL file will switch to +;; rmail-mode automatically (this is nice, but hardly essential, +;; since most people are used to explicitly running a command to +;; access their mail; M-x gnus etc). Rmail files may happen to +;; contain Local Variables sections in messages, which Rmail wants to +;; ignore. So AFAICS the only reason this variable exists is for a +;; minor convenience feature for handling of an obsolete Rmail file format. (defvar local-enable-local-variables t "Like `enable-local-variables' but meant for buffer-local bindings. The meaningful values are nil and non-nil. The default is non-nil. If a major mode sets this to nil, buffer-locally, then any local -variables list in the file will be ignored. +variables list in a file visited in that mode will be ignored. -This variable does not affect the use of major modes -specified in a -*- line.") +This variable does not affect the use of major modes specified +in a -*- line.") (defcustom enable-local-eval 'maybe "Control processing of the \"variable\" `eval' in a file's local variables. @@ -2475,17 +2497,55 @@ of a script, mode MODE is enabled. See also `auto-mode-alist'.") -(defvar inhibit-first-line-modes-regexps - (mapcar 'purecopy '("\\.tar\\'" "\\.tgz\\'" "\\.tiff?\\'" - "\\.gif\\'" "\\.png\\'" "\\.jpe?g\\'")) - "List of regexps; if one matches a file name, don't look for `-*-'. -See also `inhibit-first-line-modes-suffixes'.") - -(defvar inhibit-first-line-modes-suffixes nil - "List of regexps for what to ignore, for `inhibit-first-line-modes-regexps'. -When checking `inhibit-first-line-modes-regexps', we first discard +(define-obsolete-variable-alias 'inhibit-first-line-modes-regexps + 'inhibit-file-local-variables-regexps "24.1") + +;; TODO really this should be a list of modes (eg tar-mode), not regexps, +;; because we are duplicating info from auto-mode-alist. +;; TODO many elements of this list are also in auto-coding-alist. +(defvar inhibit-local-variables-regexps + (mapcar 'purecopy '("\\.tar\\'" "\\.t[bg]z\\'" + "\\.arc\\'" "\\.zip\\'" "\\.lzh\\'" "\\.lha\\'" + "\\.zoo\\'" "\\.[jew]ar\\'" "\\.xpi\\'" "\\.rar\\'" + "\\.7z\\'" + "\\.sx[dmicw]\\'" "\\.odt\\'" + "\\.tiff?\\'" "\\.gif\\'" "\\.png\\'" "\\.jpe?g\\'")) + "List of regexps matching file names in which to ignore local variables. +This includes `-*-' lines as well as trailing \"Local Variables\" sections. +Files matching this list are typically binary file formats. +They may happen to contain sequences that look like local variable +specifications, but are not really, or they may be containers for +member files with their own local variable sections, which are +not appropriate for the containing file. +See also `inhibit-local-variables-suffixes'.") + +(define-obsolete-variable-alias 'inhibit-first-line-modes-suffixes + 'inhibit-local-variables-suffixes "24.1") + +(defvar inhibit-local-variables-suffixes nil + "List of regexps matching suffixes to remove from file names. +When checking `inhibit-local-variables-regexps', we first discard from the end of the file name anything that matches one of these regexps.") +;; TODO explicitly add case-fold-search t? +(defun inhibit-local-variables-p () + "Return non-nil if file local variables should be ignored. +This checks the file (or buffer) name against `inhibit-local-variables-regexps' +and `inhibit-local-variables-suffixes'." + (let ((temp inhibit-local-variables-regexps) + (name (if buffer-file-name + (file-name-sans-versions buffer-file-name) + (buffer-name)))) + (while (let ((sufs inhibit-local-variables-suffixes)) + (while (and sufs (not (string-match (car sufs) name))) + (setq sufs (cdr sufs))) + sufs) + (setq name (substring name 0 (match-beginning 0)))) + (while (and temp + (not (string-match (car temp) name))) + (setq temp (cdr temp))) + temp)) + (defvar auto-mode-interpreter-regexp (purecopy "#![ \t]?\\([^ \t\n]*\ /bin/env[ \t]\\)?\\([^ \t\n]+\\)") @@ -2549,21 +2609,23 @@ Also applies to `magic-fallback-mode-alist'.") "Select major mode appropriate for current buffer. To find the right major mode, this function checks for a -*- mode tag -\(unless `inhibit-first-line-modes-regexps' says not to), checks for a `mode:' entry in the Local Variables section of the file, checks if it uses an interpreter listed in `interpreter-mode-alist', matches the buffer beginning against `magic-mode-alist', compares the filename against the entries in `auto-mode-alist', then matches the buffer beginning against `magic-fallback-mode-alist'. -If `enable-local-variables' is nil, this function does not check for -any mode: tag anywhere in the file. +If `enable-local-variables' is nil, or if the file name matches +`inhibit-local-variables-regexps', this function does not check +for any mode: tag anywhere in the file. If `local-enable-local-variables' +is nil, then the only mode: tag that can be relevant is a -*- one. If the optional argument KEEP-MODE-IF-SAME is non-nil, then we set the major mode only if that would change it. In other words we don't actually set it to the same mode the buffer already has." ;; Look for -*-MODENAME-*- or -*- ... mode: MODENAME; ... -*- - (let (end done mode modes) + (let ((try-locals (not (inhibit-local-variables-p))) + end done mode modes) ;; Once we drop the deprecated feature where mode: is also allowed to ;; specify minor-modes (ie, there can be more than one "mode:"), we can ;; remove this section and just let (hack-local-variables t) handle it. @@ -2571,7 +2633,9 @@ we don't actually set it to the same mode the buffer already has." (save-excursion (goto-char (point-min)) (skip-chars-forward " \t\n") + ;; Note by design local-enable-local-variables does not matter here. (and enable-local-variables + try-locals (setq end (set-auto-mode-1)) (if (save-excursion (search-forward ":" end t)) ;; Find all specifications for the `mode:' variable @@ -2602,8 +2666,12 @@ we don't actually set it to the same mode the buffer already has." (or (set-auto-mode-0 mode keep-mode-if-same) ;; continuing would call minor modes again, toggling them off (throw 'nop nil)))))) + ;; hack-local-variables checks local-enable-local-variables etc, but + ;; we might as well be explicit here for the sake of clarity. (and (not done) enable-local-variables + local-enable-local-variables + try-locals (setq mode (hack-local-variables t)) (not (memq mode modes)) ; already tried and failed (if (not (functionp mode)) @@ -2713,38 +2781,24 @@ same, do nothing and return nil." (defun set-auto-mode-1 () "Find the -*- spec in the buffer. Call with point at the place to start searching from. -If one is found, set point to the beginning -and return the position of the end. -Otherwise, return nil; point may be changed." +If one is found, set point to the beginning and return the position +of the end. Otherwise, return nil; may change point. +The variable `inhibit-local-variables-regexps' can cause a -*- spec to +be ignored; but `enable-local-variables' and `local-enable-local-variables' +have no effect." (let (beg end) (and ;; Don't look for -*- if this file name matches any - ;; of the regexps in inhibit-first-line-modes-regexps. - (let ((temp inhibit-first-line-modes-regexps) - (name (if buffer-file-name - (file-name-sans-versions buffer-file-name) - (buffer-name)))) - (while (let ((sufs inhibit-first-line-modes-suffixes)) - (while (and sufs (not (string-match (car sufs) name))) - (setq sufs (cdr sufs))) - sufs) - (setq name (substring name 0 (match-beginning 0)))) - (while (and temp - (not (string-match (car temp) name))) - (setq temp (cdr temp))) - (not temp)) - + ;; of the regexps in inhibit-local-variables-regexps. + (not (inhibit-local-variables-p)) (search-forward "-*-" (line-end-position - ;; If the file begins with "#!" - ;; (exec interpreter magic), look - ;; for mode frobs in the first two - ;; lines. You cannot necessarily - ;; put them in the first line of - ;; such a file without screwing up - ;; the interpreter invocation. - ;; The same holds for - ;; '\" - ;; in man pages (preprocessor + ;; If the file begins with "#!" (exec + ;; interpreter magic), look for mode frobs + ;; in the first two lines. You cannot + ;; necessarily put them in the first line + ;; of such a file without screwing up the + ;; interpreter invocation. The same holds + ;; for '\" in man pages (preprocessor ;; magic for the `man' program). (and (looking-at "^\\(#!\\|'\\\\\"\\)") 2)) t) (progn @@ -3089,19 +3143,41 @@ Uses `hack-local-variables-apply' to apply the variables. If MODE-ONLY is non-nil, all we do is check whether a \"mode:\" is specified, and return the corresponding mode symbol, or nil. In this case, we try to ignore minor-modes, and only return a -major-mode." +major-mode. + +If `enable-local-variables' or `local-enable-local-variables' is nil, +this function does nothing. If `inhibit-local-variables-regexps' +applies to the file in question, the file is not scanned for +local variables, but directory-local variables may still be applied." + ;; We don't let inhibit-local-variables-p influence the value of + ;; enable-local-variables, because then it would affect dir-local + ;; variables. We don't want to search eg tar files for file local + ;; variable sections, but there is no reason dir-locals cannot apply + ;; to them. The real meaning of inhibit-local-variables-p is "do + ;; not scan this file for local variables". (let ((enable-local-variables (and local-enable-local-variables enable-local-variables)) result) (unless mode-only (setq file-local-variables-alist nil) (report-errors "Directory-local variables error: %s" + ;; Note this is a no-op if enable-local-variables is nil. (hack-dir-local-variables))) - (when (or mode-only enable-local-variables) - ;; If MODE-ONLY is non-nil, and the prop line specifies a mode, - ;; then we're done, and have no need to scan further. - (unless (and (setq result (hack-local-variables-prop-line mode-only)) - mode-only) + ;; This entire function is basically a no-op if enable-local-variables + ;; is nil. All it does is set file-local-variables-alist to nil. + (when enable-local-variables + ;; This part used to ignore enable-local-variables when mode-only + ;; was non-nil. That was inappropriate, eg consider the + ;; (artificial) example of: + ;; (setq local-enable-local-variables nil) + ;; Open a file foo.txt that contains "mode: sh". + ;; It correctly opens in text-mode. + ;; M-x set-visited-file name foo.c, and it incorrectly stays in text-mode. + (unless (or (inhibit-local-variables-p) + ;; If MODE-ONLY is non-nil, and the prop line specifies a + ;; mode, then we're done, and have no need to scan further. + (and (setq result (hack-local-variables-prop-line mode-only)) + mode-only)) ;; Look for "Local variables:" line in last page. (save-excursion (goto-char (point-max)) @@ -3191,14 +3267,13 @@ major-mode." (indirect-variable var)) val) result) (error nil))))) - (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) - (hack-local-variables-apply))))) + (forward-line 1)))))))) + ;; Now we've read all the local variables. + ;; If MODE-ONLY is non-nil, return whether the mode was specified. + (if mode-only result + ;; Otherwise, set the variables. + (hack-local-variables-filter result nil) + (hack-local-variables-apply))))) (defun hack-local-variables-apply () "Apply the elements of `file-local-variables-alist'. @@ -3610,7 +3685,7 @@ the old visited file has been renamed to the new name FILENAME." (interactive "FSet visited file name: ") (if (buffer-base-buffer) (error "An indirect buffer cannot visit a file")) - (let (truename) + (let (truename old-try-locals) (if filename (setq filename (if (string-equal filename "") @@ -3635,7 +3710,8 @@ the old visited file has been renamed to the new name FILENAME." (progn (and filename (lock-buffer filename)) (unlock-buffer))) - (setq buffer-file-name filename) + (setq old-try-locals (not (inhibit-local-variables-p)) + buffer-file-name filename) (if filename ; make buffer name reflect filename. (let ((new-name (file-name-nondirectory buffer-file-name))) (setq default-directory (file-name-directory buffer-file-name)) @@ -3655,59 +3731,63 @@ the old visited file has been renamed to the new name FILENAME." (setq buffer-file-number (if filename (nthcdr 10 (file-attributes buffer-file-name)) - nil))) - ;; write-file-functions is normally used for things like ftp-find-file - ;; that visit things that are not local files as if they were files. - ;; Changing to visit an ordinary local file instead should flush the hook. - (kill-local-variable 'write-file-functions) - (kill-local-variable 'local-write-file-hooks) - (kill-local-variable 'revert-buffer-function) - (kill-local-variable 'backup-inhibited) - ;; If buffer was read-only because of version control, - ;; that reason is gone now, so make it writable. - (if vc-mode - (setq buffer-read-only nil)) - (kill-local-variable 'vc-mode) - ;; Turn off backup files for certain file names. - ;; Since this is a permanent local, the major mode won't eliminate it. - (and buffer-file-name - backup-enable-predicate - (not (funcall backup-enable-predicate buffer-file-name)) - (progn - (make-local-variable 'backup-inhibited) - (setq backup-inhibited t))) - (let ((oauto buffer-auto-save-file-name)) - ;; If auto-save was not already on, turn it on if appropriate. - (if (not buffer-auto-save-file-name) - (and buffer-file-name auto-save-default - (auto-save-mode t)) - ;; If auto save is on, start using a new name. - ;; We deliberately don't rename or delete the old auto save - ;; for the old visited file name. This is because perhaps - ;; the user wants to save the new state and then compare with the - ;; previous state from the auto save file. - (setq buffer-auto-save-file-name - (make-auto-save-file-name))) - ;; Rename the old auto save file if any. - (and oauto buffer-auto-save-file-name - (file-exists-p oauto) - (rename-file oauto buffer-auto-save-file-name t))) - (and buffer-file-name - (not along-with-file) - (set-buffer-modified-p t)) - ;; Update the major mode, if the file name determines it. - (condition-case nil - ;; Don't change the mode if it is special. - (or (not change-major-mode-with-file-name) - (get major-mode 'mode-class) - ;; Don't change the mode if the local variable list specifies it. - (hack-local-variables t) - ;; TODO consider making normal-mode handle this case. - (let ((old major-mode)) - (set-auto-mode t) - (or (eq old major-mode) - (hack-local-variables)))) - (error nil))) + nil)) + ;; write-file-functions is normally used for things like ftp-find-file + ;; that visit things that are not local files as if they were files. + ;; Changing to visit an ordinary local file instead should flush the hook. + (kill-local-variable 'write-file-functions) + (kill-local-variable 'local-write-file-hooks) + (kill-local-variable 'revert-buffer-function) + (kill-local-variable 'backup-inhibited) + ;; If buffer was read-only because of version control, + ;; that reason is gone now, so make it writable. + (if vc-mode + (setq buffer-read-only nil)) + (kill-local-variable 'vc-mode) + ;; Turn off backup files for certain file names. + ;; Since this is a permanent local, the major mode won't eliminate it. + (and buffer-file-name + backup-enable-predicate + (not (funcall backup-enable-predicate buffer-file-name)) + (progn + (make-local-variable 'backup-inhibited) + (setq backup-inhibited t))) + (let ((oauto buffer-auto-save-file-name)) + ;; If auto-save was not already on, turn it on if appropriate. + (if (not buffer-auto-save-file-name) + (and buffer-file-name auto-save-default + (auto-save-mode t)) + ;; If auto save is on, start using a new name. + ;; We deliberately don't rename or delete the old auto save + ;; for the old visited file name. This is because perhaps + ;; the user wants to save the new state and then compare with the + ;; previous state from the auto save file. + (setq buffer-auto-save-file-name + (make-auto-save-file-name))) + ;; Rename the old auto save file if any. + (and oauto buffer-auto-save-file-name + (file-exists-p oauto) + (rename-file oauto buffer-auto-save-file-name t))) + (and buffer-file-name + (not along-with-file) + (set-buffer-modified-p t)) + ;; Update the major mode, if the file name determines it. + (condition-case nil + ;; Don't change the mode if it is special. + (or (not change-major-mode-with-file-name) + (get major-mode 'mode-class) + ;; Don't change the mode if the local variable list specifies it. + ;; The file name can influence whether the local variables apply. + (and old-try-locals + ;; h-l-v also checks it, but might as well be explcit. + (not (inhibit-local-variables-p)) + (hack-local-variables t)) + ;; TODO consider making normal-mode handle this case. + (let ((old major-mode)) + (set-auto-mode t) + (or (eq old major-mode) + (hack-local-variables)))) + (error nil)))) (defun write-file (filename &optional confirm) "Write current buffer into file FILENAME. diff --git a/lisp/jka-cmpr-hook.el b/lisp/jka-cmpr-hook.el index d09e64634c..600ed54973 100644 --- a/lisp/jka-cmpr-hook.el +++ b/lisp/jka-cmpr-hook.el @@ -119,7 +119,7 @@ based on the filename itself and `jka-compr-compression-info-list'." (defun jka-compr-install () "Install jka-compr. This adds entries to `file-name-handler-alist' and `auto-mode-alist' -and `inhibit-first-line-modes-suffixes'." +and `inhibit-local-variables-suffixes'." (setq jka-compr-file-name-handler-entry (cons (jka-compr-build-file-regexp) 'jka-compr-handler)) @@ -145,12 +145,12 @@ and `inhibit-first-line-modes-suffixes'." ;; are chosen right according to the file names ;; sans `.gz'. (push (list (jka-compr-info-regexp x) nil 'jka-compr) auto-mode-alist) - ;; Also add these regexps to - ;; inhibit-first-line-modes-suffixes, so that a - ;; -*- line in the first file of a compressed tar - ;; file doesn't override tar-mode. + ;; Also add these regexps to inhibit-local-variables-suffixes, + ;; so that a -*- line in the first file of a compressed tar file, + ;; or a Local Variables section in a member file at the end of + ;; the tar file don't override tar-mode. (push (jka-compr-info-regexp x) - inhibit-first-line-modes-suffixes))) + inhibit-local-variables-suffixes))) (setq auto-mode-alist (append auto-mode-alist jka-compr-mode-alist-additions)) diff --git a/lisp/jka-compr.el b/lisp/jka-compr.el index 786e4292d5..8a8d7cdbb5 100644 --- a/lisp/jka-compr.el +++ b/lisp/jka-compr.el @@ -657,16 +657,15 @@ It is not recommended to set this variable permanently to anything but nil.") (defun jka-compr-uninstall () "Uninstall jka-compr. This removes the entries in `file-name-handler-alist' and `auto-mode-alist' -and `inhibit-first-line-modes-suffixes' that were added +and `inhibit-local-variables-suffixes' that were added by `jka-compr-installed'." - ;; Delete from inhibit-first-line-modes-suffixes - ;; what jka-compr-install added. + ;; Delete from inhibit-local-variables-suffixes what jka-compr-install added. (mapc (function (lambda (x) (and (jka-compr-info-strip-extension x) - (setq inhibit-first-line-modes-suffixes + (setq inhibit-local-variables-suffixes (delete (jka-compr-info-regexp x) - inhibit-first-line-modes-suffixes))))) + inhibit-local-variables-suffixes))))) jka-compr-compression-info-list--internal) (let* ((fnha (cons nil file-name-handler-alist)) -- 2.20.1