* Updated project-specific settings patch
@ 2008-05-19 17:07 Tom Tromey
2008-05-19 18:51 ` Stefan Monnier
` (2 more replies)
0 siblings, 3 replies; 25+ messages in thread
From: Tom Tromey @ 2008-05-19 17:07 UTC (permalink / raw
To: emacs-devel
A long while ago I was working on a patch to allow project-specific
settings for Emacs. Here's the updated version of this patch.
I tried to incorporate all the feedback on previous versions of this
patch into the current patch. In particular:
* Now a patch to files.el, not a separate file.
* Reuses the hack-local-variables code to query the user about risky
settings.
* Search for directory settings is limited to a single file name,
".dir-settings.el".
Tom
lisp/ChangeLog:
2008-05-19 Tom Tromey <tromey@opsy>
* files.el (normal-mode): Call hack-project-variables.
(hack-local-variables-confirm): Add 'project' argument.
(hack-local-variables-apply): New function.
(hack-local-variables): Use it.
(project-class-alist, project-directory-alist): New variables.
(project-get-alist): New function.
(project-collect-bindings-from-alist)
(project-collect-binding-list, set-directory-project)
(project-find-settings-file, project-define-from-project-file)
(hack-project-variables): New functions.
doc/emacs/ChangeLog:
2008-05-19 Tom Tromey <tromey@opsy>
* custom.texi (Variables): Add Directory Variables to menu.
(Directory Variables): New node.
etc/ChangeLog:
2008-05-19 Tom Tromey <tromey@redhat.com>
* NEWS: Mention directory-local variables.
Index: lisp/files.el
===================================================================
RCS file: /sources/emacs/emacs/lisp/files.el,v
retrieving revision 1.976
diff -u -r1.976 files.el
--- lisp/files.el 6 May 2008 07:57:35 -0000 1.976
+++ lisp/files.el 19 May 2008 17:04:22 -0000
@@ -1973,6 +1973,8 @@
(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
@@ -2623,11 +2625,13 @@
(put 'c-set-style 'safe-local-eval-function t)
-(defun hack-local-variables-confirm (all-vars unsafe-vars risky-vars)
+(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."
+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."
(if noninteractive
nil
(let ((name (if buffer-file-name
@@ -2636,20 +2640,22 @@
(offer-save (and (eq enable-local-variables t) unsafe-vars))
prompt char)
(save-window-excursion
- (let ((buf (get-buffer-create "*Local Variables*")))
+ (let ((buf (get-buffer-create (if project "*Directory Variabes*"
+ "*Local Variables*"))))
(pop-to-buffer buf)
(set (make-local-variable 'cursor-type) nil)
(erase-buffer)
(if unsafe-vars
- (insert "The local variables list in " name
+ (insert "The local variables list in " (or project name)
"\ncontains values that may not be safe (*)"
(if risky-vars
", and variables that are risky (**)."
"."))
(if risky-vars
- (insert "The local variables list in " name
+ (insert "The local variables list in " (or project name)
"\ncontains variables that are risky (**).")
- (insert "A local variables list is specified in " name ".")))
+ (insert "A local variables list is specified in "
+ (or project name) ".")))
(insert "\n\nDo you want to apply it? You can type
y -- to apply the local variables list.
n -- to ignore the local variables list.")
@@ -2771,6 +2777,50 @@
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)
+ (or (eq enable-local-eval t)
+ (hack-one-local-variable-eval-safep
+ (eval (quote val)))))
+ (safe-local-variable-p var val)
+ (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)
+ (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))))))))
+
(defun hack-local-variables (&optional mode-only)
"Parse and put into effect this buffer's local variables spec.
If MODE-ONLY is non-nil, all we do is check whether the major mode
@@ -2862,45 +2912,7 @@
;; variables (if MODE-ONLY is nil.)
(if mode-only
result
- (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)
- (or (eq enable-local-eval t)
- (hack-one-local-variable-eval-safep
- (eval (quote val)))))
- (safe-local-variable-p var val)
- (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 quuery in that case.
- (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))
- (dolist (elt result)
- (hack-one-local-variable (car elt) (cdr elt)))))))
+ (hack-local-variables-apply result nil)
(run-hooks 'hack-local-variables-hook)))))
(defun safe-local-variable-p (sym val)
@@ -3004,6 +3016,167 @@
(if (stringp val)
(set-text-properties 0 (length val) nil val))
(set (make-local-variable var) val))))
+\f
+;;; Handling directory local variables, aka project settings.
+
+(defvar project-class-alist '()
+ "Alist mapping project class names (symbols) to project variable lists.")
+
+(defvar project-directory-alist '()
+ "Alist mapping project directory roots to project classes.")
+
+(defsubst project-get-alist (class)
+ "Return the project variable list for project CLASS."
+ (cdr (assq class project-class-alist)))
+
+(defun project-collect-bindings-from-alist (mode-alist settings)
+ "Collect local variable settings from MODE-ALIST.
+SETTINGS is the initial list of bindings.
+Returns the new list."
+ (dolist (pair mode-alist settings)
+ (let* ((variable (car pair))
+ (value (cdr pair))
+ (slot (assq variable settings)))
+ (if slot
+ (setcdr slot value)
+ ;; Need a new cons in case we setcdr later.
+ (push (cons variable value) settings)))))
+
+(defun project-collect-binding-list (binding-list root settings)
+ "Collect entries from BINDING-LIST into SETTINGS.
+ROOT is the root directory of the project.
+Return the new settings list."
+ (let* ((file-name (buffer-file-name))
+ (sub-file-name (if file-name
+ (substring file-name (length root)))))
+ (dolist (entry binding-list settings)
+ (let ((key (car entry)))
+ (cond
+ ((stringp key)
+ ;; Don't include this in the previous condition, because we
+ ;; want to filter all strings before the next condition.
+ (when (and sub-file-name
+ (>= (length sub-file-name) (length key))
+ (string= key (substring sub-file-name 0 (length key))))
+ (setq settings (project-collect-binding-list (cdr entry)
+ root settings))))
+ ((or (not key)
+ (derived-mode-p key))
+ (setq settings (project-collect-bindings-from-alist (cdr entry)
+ settings))))))))
+
+(defun set-directory-project (directory class)
+ "Declare that the project rooted at DIRECTORY is an instance of CLASS.
+DIRECTORY is the name of a directory, a string.
+CLASS is the name of a project class, a symbol.
+
+When a file beneath DIRECTORY is visited, the mode-specific
+settings from CLASS will be applied to the buffer. The settings
+for a class are defined using `define-project-bindings'."
+ (setq directory (file-name-as-directory (expand-file-name directory)))
+ (unless (assq class project-class-alist)
+ (error "No such project class `%s'" (symbol-name class)))
+ (push (cons directory class) project-directory-alist))
+
+(defun define-project-bindings (class list)
+ "Map the project type CLASS to a list of variable settings.
+CLASS is the project class, a symbol.
+LIST is a list that declares variable settings for the class.
+An element in LIST is either of the form:
+ (MAJOR-MODE . ALIST)
+or
+ (DIRECTORY . LIST)
+
+In the first form, MAJOR-MODE is a symbol, and ALIST is an alist
+whose elements are of the form (VARIABLE . VALUE).
+
+In the second form, DIRECTORY is a directory name (a string), and
+LIST is a list of the form accepted by the function.
+
+When a file is visited, the file's class is found. A directory
+may be assigned a class using `set-directory-project'. Then
+variables are set in the file's buffer according to the class'
+LIST. The list is processed in order.
+
+* If the element is of the form (MAJOR-MODE . ALIST), and the
+ buffer's major mode is derived from MAJOR-MODE (as determined
+ by `derived-mode-p'), then all the settings in ALIST are
+ applied. A MAJOR-MODE of nil may be used to match any buffer.
+ `make-local-variable' is called for each variable before it is
+ set.
+
+* If the element is of the form (DIRECTORY . LIST), and DIRECTORY
+ is an initial substring of the file's directory, then LIST is
+ applied by recursively following these rules."
+ (let ((elt (assq class project-class-alist)))
+ (if elt
+ (setcdr elt list)
+ (push (cons class list) project-class-alist))))
+
+(defun project-find-settings-file (file)
+ "Find the settings file for FILE.
+This searches upward in the directory tree.
+If a settings file is found, the file name is returned.
+If the file is in a registered project, a cons from
+`project-directory-alist' is returned.
+Otherwise this returns nil."
+ (let ((dir (file-name-directory file))
+ (result nil))
+ (while (and (not (string= dir "/"))
+ (not result))
+ (cond
+ ((setq result (assoc dir project-directory-alist))
+ ;; Nothing else.
+ nil)
+ ((file-exists-p (concat dir ".dir-settings.el"))
+ (setq result (concat dir ".dir-settings.el")))
+ (t
+ (setq dir (file-name-directory (directory-file-name dir))))))
+ result))
+
+(defun project-define-from-project-file (settings-file)
+ "Load a settings file and register a new project class and instance.
+SETTINGS-FILE is the name of the file holding the settings to apply.
+The new class name is the same as the directory in which SETTINGS-FILE
+is found. Returns the new class name."
+ (with-temp-buffer
+ ;; We should probably store the modtime of SETTINGS-FILE and then
+ ;; reload it whenever it changes.
+ (insert-file-contents settings-file)
+ (let* ((dir-name (file-name-directory settings-file))
+ (class-name (intern dir-name))
+ (list (read (current-buffer))))
+ (define-project-bindings class-name list)
+ (set-directory-project dir-name class-name)
+ class-name)))
+
+(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))))
+ ;; Find the settings file.
+ (let ((settings (project-find-settings-file (buffer-file-name)))
+ (class nil)
+ (root-dir nil))
+ (cond
+ ((stringp settings)
+ (setq root-dir (file-name-directory (buffer-file-name)))
+ (setq class (project-define-from-project-file settings)))
+ ((consp settings)
+ (setq root-dir (car settings))
+ (setq class (cdr settings))))
+ (when class
+ (let ((bindings
+ (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))))))))
\f
(defcustom change-major-mode-with-file-name t
Index: doc/emacs/custom.texi
===================================================================
RCS file: /sources/emacs/emacs/doc/emacs/custom.texi,v
retrieving revision 1.9
diff -u -r1.9 custom.texi
--- doc/emacs/custom.texi 5 Apr 2008 23:01:19 -0000 1.9
+++ doc/emacs/custom.texi 19 May 2008 17:04:23 -0000
@@ -796,6 +796,7 @@
of Emacs to run on particular occasions.
* Locals:: Per-buffer values of variables.
* File Variables:: How files can specify variable values.
+* Directory Variables:: How variable values can be specified by directory.
@end menu
@node Examining
@@ -1262,6 +1263,65 @@
for confirmation when it finds these forms for the @code{eval}
variable.
+@node Directory Variables
+@subsection Per-Directory Local Variables
+@cindex local variables in directories
+@cindex directory local variables
+
+ Emacs provides a way to specify local variable values per-directory.
+This can be done one of two ways.
+
+ The first approach is to put a special file, named
+@file{.dir-settings.el}, in a directory. When opening a file, Emacs
+searches for @file{.dir-settings.el} starting in the file's directory
+and then moving up the directory hierarchy. If
+@file{.dir-settings.el} is found, Emacs applies variable settings from
+the file to the new buffer. If the file is remote, Emacs skips this
+search, because it would be too slow.
+
+ The file should hold a specially-constructed list. This list maps
+Emacs mode names (symbols) to alists; each alist maps variable names
+to values. The special mode name @samp{nil} means that the alist
+should be applied to all buffers. Finally, a string key can be used
+to specify an alist which applies to a relative subdirectory in the
+project.
+
+@example
+((nil . ((indent-tabs-mode . t)
+ (tab-width . 4)
+ (fill-column . 80)))
+ (c-mode . ((c-file-style . "BSD")))
+ (java-mode . ((c-file-style . "BSD")))
+ ("src/imported"
+ . ((nil . ((change-log-default-name . "ChangeLog.local"))))))
+@end example
+
+ This example shows some settings for a hypothetical project. This
+sets @samp{indent-tabs-mode} to @samp{t} for any file in the source
+tree, and it sets the indentation style for any C or Java source file
+to @samp{BSD}. Finally, it specifies a different @file{ChangeLog}
+file name for any file in the project that appears beneath the
+directory @file{src/imported}.
+
+ The second approach to directory-local settings is to explicitly
+define a project class using @code{define-project-bindings}, and then
+to tell Emacs which directory roots correspond to that class, using
+@code{set-directory-project}. You can put calls to these functions in
+your @file{.emacs}; this can useful when you can't put
+@file{.dir-settings.el} in the directory for some reason. For
+example, you could apply settings to an unwriteable directory this
+way:
+
+@example
+(define-project-bindings 'unwriteable-directory
+ '((nil . ((some-useful-setting . value)))))
+
+(set-directory-project "/usr/include/" 'unwriteable-directory)
+@end example
+
+ Unsafe directory-local variables are handled in the same way as
+unsafe file-local variables.
+
@node Key Bindings
@section Customizing Key Bindings
@cindex key bindings
Index: etc/NEWS
===================================================================
RCS file: /sources/emacs/emacs/etc/NEWS,v
retrieving revision 1.1746
diff -u -r1.1746 NEWS
--- etc/NEWS 15 May 2008 07:26:32 -0000 1.1746
+++ etc/NEWS 19 May 2008 17:04:23 -0000
@@ -180,6 +180,9 @@
** The new command `display-time-world' starts an updating time display
using several time zones, in a buffer.
+** Directory-local variables are now found in .dir-settings.el. See
+also `set-directory-project' and `define-project-bindings'.
+
** The new function `format-seconds' converts a number of seconds into a
readable string of days, hours, etc.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-19 17:07 Updated project-specific settings patch Tom Tromey
@ 2008-05-19 18:51 ` Stefan Monnier
2008-05-19 18:55 ` Miles Bader
` (2 more replies)
2008-05-19 19:32 ` joakim
2008-05-19 20:02 ` Chong Yidong
2 siblings, 3 replies; 25+ messages in thread
From: Stefan Monnier @ 2008-05-19 18:51 UTC (permalink / raw
To: tromey; +Cc: emacs-devel
> A long while ago I was working on a patch to allow project-specific
> settings for Emacs. Here's the updated version of this patch.
> I tried to incorporate all the feedback on previous versions of this
> patch into the current patch. In particular:
> * Now a patch to files.el, not a separate file.
> * Reuses the hack-local-variables code to query the user about risky
> settings.
> * Search for directory settings is limited to a single file name,
> ".dir-settings.el".
Thank you. It looks good. I just have a few minor wishes below, but
otherwise, feel free to install it.
> + (let ((buf (get-buffer-create (if project "*Directory Variabes*"
> + "*Local Variables*"))))
Better use the same buffer name *Local Variables*.
> + ((file-exists-p (concat dir ".dir-settings.el"))
> + (setq result (concat dir ".dir-settings.el")))
In most cases, we'd prefer (expand-file-name ".dir-settings.el" dir)
over the use of `concat'. The only exception I know is when `dir' is
relative and it's important for the result also be relative.
One more thing: since we want to include CEDET and CEDET has its own
notion of "project data", we want to try and get those two things
to converge.
So please try and work out with zappo@gnu.org what convergence might
look like.
Stefan
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-19 18:51 ` Stefan Monnier
@ 2008-05-19 18:55 ` Miles Bader
2008-05-19 19:00 ` Tom Tromey
2008-05-19 19:23 ` Tom Tromey
2008-05-19 20:29 ` David Kastrup
2 siblings, 1 reply; 25+ messages in thread
From: Miles Bader @ 2008-05-19 18:55 UTC (permalink / raw
To: Stefan Monnier; +Cc: tromey, emacs-devel
Stefan Monnier <monnier@iro.umontreal.ca> writes:
>> * Search for directory settings is limited to a single file name,
>> ".dir-settings.el".
Should this be called ".emacs-settings.el"? I guess the ".el" is
supposed to imply that, but it might not be so obvious to everybody...
-Miles
--
We live, as we dream -- alone....
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-19 18:55 ` Miles Bader
@ 2008-05-19 19:00 ` Tom Tromey
2008-05-20 2:35 ` Miles Bader
0 siblings, 1 reply; 25+ messages in thread
From: Tom Tromey @ 2008-05-19 19:00 UTC (permalink / raw
To: Miles Bader; +Cc: Stefan Monnier, emacs-devel
>>>>> "Miles" == Miles Bader <miles@gnu.org> writes:
>>> * Search for directory settings is limited to a single file name,
>>> ".dir-settings.el".
Miles> Should this be called ".emacs-settings.el"? I guess the ".el" is
Miles> supposed to imply that, but it might not be so obvious to everybody...
There were a couple long threads about the naming. My recollection is
that RMS required that there be a single name and that it not include
the word "emacs". Given those constraints, this is what I picked.
I can dig up references if you want. If we're going to reopen the
naming discussion, I would like to request that everybody go and read
the whole thread for themselves before making suggestions.
Tom
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-19 18:51 ` Stefan Monnier
2008-05-19 18:55 ` Miles Bader
@ 2008-05-19 19:23 ` Tom Tromey
2008-05-19 19:51 ` Re[2]: Project local variables & EDE (was: Updated project-specific settings patch) Eric M. Ludlam
2008-05-19 22:41 ` Updated project-specific settings patch Michael Olson
2008-05-19 20:29 ` David Kastrup
2 siblings, 2 replies; 25+ messages in thread
From: Tom Tromey @ 2008-05-19 19:23 UTC (permalink / raw
To: Stefan Monnier; +Cc: zappo, emacs-devel
>>>>> "Stefan" == Stefan Monnier <monnier@iro.umontreal.ca> writes:
Stefan> Thank you. It looks good. I just have a few minor wishes
Stefan> below, but otherwise, feel free to install it.
I don't have write access. I was told off-list to apply for it, but I
don't feel that comfortable doing so -- my patch acceptance rate has
not really been all that great. However, if you think I ought to do
that, let me know and I will.
>> + (let ((buf (get-buffer-create (if project "*Directory Variabes*"
>> + "*Local Variables*"))))
Stefan> Better use the same buffer name *Local Variables*.
I made this change. And anyway I had a typo in the new buffer name --
oops.
>> + ((file-exists-p (concat dir ".dir-settings.el"))
>> + (setq result (concat dir ".dir-settings.el")))
Stefan> In most cases, we'd prefer (expand-file-name ".dir-settings.el" dir)
Stefan> over the use of `concat'. The only exception I know is when `dir' is
Stefan> relative and it's important for the result also be relative.
No problem.
New patch appended.
Stefan> One more thing: since we want to include CEDET and CEDET has its own
Stefan> notion of "project data", we want to try and get those two things
Stefan> to converge.
Stefan> So please try and work out with zappo@gnu.org what convergence might
Stefan> look like.
I've CC'd him.
I looked through CEDET a bit but project-local settings do not seem to
be extensively documented. I think you have to use EDE to get them,
and from what I can see EDE only supports a couple types of projects.
Also, it appears that a Project.ede is needed in every directory.
Confirmation (or anti-confirmation) of these assertions would be
helpful.
Tom
lisp/ChangeLog:
2008-05-19 Tom Tromey <tromey@opsy>
* files.el (normal-mode): Call hack-project-variables.
(hack-local-variables-confirm): Add 'project' argument.
(hack-local-variables-apply): New function.
(hack-local-variables): Use it.
(project-class-alist, project-directory-alist): New variables.
(project-get-alist): New function.
(project-collect-bindings-from-alist)
(project-collect-binding-list, set-directory-project)
(project-find-settings-file, project-define-from-project-file)
(hack-project-variables): New functions.
doc/emacs/ChangeLog:
2008-05-19 Tom Tromey <tromey@opsy>
* custom.texi (Variables): Add Directory Variables to menu.
(Directory Variables): New node.
etc/ChangeLog:
2008-05-19 Tom Tromey <tromey@redhat.com>
* NEWS: Mention directory-local variables.
Index: lisp/files.el
===================================================================
RCS file: /sources/emacs/emacs/lisp/files.el,v
retrieving revision 1.976
diff -u -r1.976 files.el
--- lisp/files.el 6 May 2008 07:57:35 -0000 1.976
+++ lisp/files.el 19 May 2008 19:20:46 -0000
@@ -1973,6 +1973,8 @@
(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
@@ -2623,11 +2625,13 @@
(put 'c-set-style 'safe-local-eval-function t)
-(defun hack-local-variables-confirm (all-vars unsafe-vars risky-vars)
+(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."
+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."
(if noninteractive
nil
(let ((name (if buffer-file-name
@@ -2641,15 +2645,16 @@
(set (make-local-variable 'cursor-type) nil)
(erase-buffer)
(if unsafe-vars
- (insert "The local variables list in " name
+ (insert "The local variables list in " (or project name)
"\ncontains values that may not be safe (*)"
(if risky-vars
", and variables that are risky (**)."
"."))
(if risky-vars
- (insert "The local variables list in " name
+ (insert "The local variables list in " (or project name)
"\ncontains variables that are risky (**).")
- (insert "A local variables list is specified in " name ".")))
+ (insert "A local variables list is specified in "
+ (or project name) ".")))
(insert "\n\nDo you want to apply it? You can type
y -- to apply the local variables list.
n -- to ignore the local variables list.")
@@ -2771,6 +2776,50 @@
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)
+ (or (eq enable-local-eval t)
+ (hack-one-local-variable-eval-safep
+ (eval (quote val)))))
+ (safe-local-variable-p var val)
+ (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)
+ (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))))))))
+
(defun hack-local-variables (&optional mode-only)
"Parse and put into effect this buffer's local variables spec.
If MODE-ONLY is non-nil, all we do is check whether the major mode
@@ -2862,45 +2911,7 @@
;; variables (if MODE-ONLY is nil.)
(if mode-only
result
- (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)
- (or (eq enable-local-eval t)
- (hack-one-local-variable-eval-safep
- (eval (quote val)))))
- (safe-local-variable-p var val)
- (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 quuery in that case.
- (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))
- (dolist (elt result)
- (hack-one-local-variable (car elt) (cdr elt)))))))
+ (hack-local-variables-apply result nil)
(run-hooks 'hack-local-variables-hook)))))
(defun safe-local-variable-p (sym val)
@@ -3004,6 +3015,167 @@
(if (stringp val)
(set-text-properties 0 (length val) nil val))
(set (make-local-variable var) val))))
+\f
+;;; Handling directory local variables, aka project settings.
+
+(defvar project-class-alist '()
+ "Alist mapping project class names (symbols) to project variable lists.")
+
+(defvar project-directory-alist '()
+ "Alist mapping project directory roots to project classes.")
+
+(defsubst project-get-alist (class)
+ "Return the project variable list for project CLASS."
+ (cdr (assq class project-class-alist)))
+
+(defun project-collect-bindings-from-alist (mode-alist settings)
+ "Collect local variable settings from MODE-ALIST.
+SETTINGS is the initial list of bindings.
+Returns the new list."
+ (dolist (pair mode-alist settings)
+ (let* ((variable (car pair))
+ (value (cdr pair))
+ (slot (assq variable settings)))
+ (if slot
+ (setcdr slot value)
+ ;; Need a new cons in case we setcdr later.
+ (push (cons variable value) settings)))))
+
+(defun project-collect-binding-list (binding-list root settings)
+ "Collect entries from BINDING-LIST into SETTINGS.
+ROOT is the root directory of the project.
+Return the new settings list."
+ (let* ((file-name (buffer-file-name))
+ (sub-file-name (if file-name
+ (substring file-name (length root)))))
+ (dolist (entry binding-list settings)
+ (let ((key (car entry)))
+ (cond
+ ((stringp key)
+ ;; Don't include this in the previous condition, because we
+ ;; want to filter all strings before the next condition.
+ (when (and sub-file-name
+ (>= (length sub-file-name) (length key))
+ (string= key (substring sub-file-name 0 (length key))))
+ (setq settings (project-collect-binding-list (cdr entry)
+ root settings))))
+ ((or (not key)
+ (derived-mode-p key))
+ (setq settings (project-collect-bindings-from-alist (cdr entry)
+ settings))))))))
+
+(defun set-directory-project (directory class)
+ "Declare that the project rooted at DIRECTORY is an instance of CLASS.
+DIRECTORY is the name of a directory, a string.
+CLASS is the name of a project class, a symbol.
+
+When a file beneath DIRECTORY is visited, the mode-specific
+settings from CLASS will be applied to the buffer. The settings
+for a class are defined using `define-project-bindings'."
+ (setq directory (file-name-as-directory (expand-file-name directory)))
+ (unless (assq class project-class-alist)
+ (error "No such project class `%s'" (symbol-name class)))
+ (push (cons directory class) project-directory-alist))
+
+(defun define-project-bindings (class list)
+ "Map the project type CLASS to a list of variable settings.
+CLASS is the project class, a symbol.
+LIST is a list that declares variable settings for the class.
+An element in LIST is either of the form:
+ (MAJOR-MODE . ALIST)
+or
+ (DIRECTORY . LIST)
+
+In the first form, MAJOR-MODE is a symbol, and ALIST is an alist
+whose elements are of the form (VARIABLE . VALUE).
+
+In the second form, DIRECTORY is a directory name (a string), and
+LIST is a list of the form accepted by the function.
+
+When a file is visited, the file's class is found. A directory
+may be assigned a class using `set-directory-project'. Then
+variables are set in the file's buffer according to the class'
+LIST. The list is processed in order.
+
+* If the element is of the form (MAJOR-MODE . ALIST), and the
+ buffer's major mode is derived from MAJOR-MODE (as determined
+ by `derived-mode-p'), then all the settings in ALIST are
+ applied. A MAJOR-MODE of nil may be used to match any buffer.
+ `make-local-variable' is called for each variable before it is
+ set.
+
+* If the element is of the form (DIRECTORY . LIST), and DIRECTORY
+ is an initial substring of the file's directory, then LIST is
+ applied by recursively following these rules."
+ (let ((elt (assq class project-class-alist)))
+ (if elt
+ (setcdr elt list)
+ (push (cons class list) project-class-alist))))
+
+(defun project-find-settings-file (file)
+ "Find the settings file for FILE.
+This searches upward in the directory tree.
+If a settings file is found, the file name is returned.
+If the file is in a registered project, a cons from
+`project-directory-alist' is returned.
+Otherwise this returns nil."
+ (let ((dir (file-name-directory file))
+ (result nil))
+ (while (and (not (string= dir "/"))
+ (not result))
+ (cond
+ ((setq result (assoc dir project-directory-alist))
+ ;; Nothing else.
+ nil)
+ ((file-exists-p (expand-file-name ".dir-settings.el" dir))
+ (setq result (expand-file-name ".dir-settings.el" dir)))
+ (t
+ (setq dir (file-name-directory (directory-file-name dir))))))
+ result))
+
+(defun project-define-from-project-file (settings-file)
+ "Load a settings file and register a new project class and instance.
+SETTINGS-FILE is the name of the file holding the settings to apply.
+The new class name is the same as the directory in which SETTINGS-FILE
+is found. Returns the new class name."
+ (with-temp-buffer
+ ;; We should probably store the modtime of SETTINGS-FILE and then
+ ;; reload it whenever it changes.
+ (insert-file-contents settings-file)
+ (let* ((dir-name (file-name-directory settings-file))
+ (class-name (intern dir-name))
+ (list (read (current-buffer))))
+ (define-project-bindings class-name list)
+ (set-directory-project dir-name class-name)
+ class-name)))
+
+(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))))
+ ;; Find the settings file.
+ (let ((settings (project-find-settings-file (buffer-file-name)))
+ (class nil)
+ (root-dir nil))
+ (cond
+ ((stringp settings)
+ (setq root-dir (file-name-directory (buffer-file-name)))
+ (setq class (project-define-from-project-file settings)))
+ ((consp settings)
+ (setq root-dir (car settings))
+ (setq class (cdr settings))))
+ (when class
+ (let ((bindings
+ (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))))))))
\f
(defcustom change-major-mode-with-file-name t
Index: doc/emacs/custom.texi
===================================================================
RCS file: /sources/emacs/emacs/doc/emacs/custom.texi,v
retrieving revision 1.9
diff -u -r1.9 custom.texi
--- doc/emacs/custom.texi 5 Apr 2008 23:01:19 -0000 1.9
+++ doc/emacs/custom.texi 19 May 2008 19:20:47 -0000
@@ -796,6 +796,7 @@
of Emacs to run on particular occasions.
* Locals:: Per-buffer values of variables.
* File Variables:: How files can specify variable values.
+* Directory Variables:: How variable values can be specified by directory.
@end menu
@node Examining
@@ -1262,6 +1263,65 @@
for confirmation when it finds these forms for the @code{eval}
variable.
+@node Directory Variables
+@subsection Per-Directory Local Variables
+@cindex local variables in directories
+@cindex directory local variables
+
+ Emacs provides a way to specify local variable values per-directory.
+This can be done one of two ways.
+
+ The first approach is to put a special file, named
+@file{.dir-settings.el}, in a directory. When opening a file, Emacs
+searches for @file{.dir-settings.el} starting in the file's directory
+and then moving up the directory hierarchy. If
+@file{.dir-settings.el} is found, Emacs applies variable settings from
+the file to the new buffer. If the file is remote, Emacs skips this
+search, because it would be too slow.
+
+ The file should hold a specially-constructed list. This list maps
+Emacs mode names (symbols) to alists; each alist maps variable names
+to values. The special mode name @samp{nil} means that the alist
+should be applied to all buffers. Finally, a string key can be used
+to specify an alist which applies to a relative subdirectory in the
+project.
+
+@example
+((nil . ((indent-tabs-mode . t)
+ (tab-width . 4)
+ (fill-column . 80)))
+ (c-mode . ((c-file-style . "BSD")))
+ (java-mode . ((c-file-style . "BSD")))
+ ("src/imported"
+ . ((nil . ((change-log-default-name . "ChangeLog.local"))))))
+@end example
+
+ This example shows some settings for a hypothetical project. This
+sets @samp{indent-tabs-mode} to @samp{t} for any file in the source
+tree, and it sets the indentation style for any C or Java source file
+to @samp{BSD}. Finally, it specifies a different @file{ChangeLog}
+file name for any file in the project that appears beneath the
+directory @file{src/imported}.
+
+ The second approach to directory-local settings is to explicitly
+define a project class using @code{define-project-bindings}, and then
+to tell Emacs which directory roots correspond to that class, using
+@code{set-directory-project}. You can put calls to these functions in
+your @file{.emacs}; this can useful when you can't put
+@file{.dir-settings.el} in the directory for some reason. For
+example, you could apply settings to an unwriteable directory this
+way:
+
+@example
+(define-project-bindings 'unwriteable-directory
+ '((nil . ((some-useful-setting . value)))))
+
+(set-directory-project "/usr/include/" 'unwriteable-directory)
+@end example
+
+ Unsafe directory-local variables are handled in the same way as
+unsafe file-local variables.
+
@node Key Bindings
@section Customizing Key Bindings
@cindex key bindings
Index: etc/NEWS
===================================================================
RCS file: /sources/emacs/emacs/etc/NEWS,v
retrieving revision 1.1746
diff -u -r1.1746 NEWS
--- etc/NEWS 15 May 2008 07:26:32 -0000 1.1746
+++ etc/NEWS 19 May 2008 19:20:48 -0000
@@ -180,6 +180,9 @@
** The new command `display-time-world' starts an updating time display
using several time zones, in a buffer.
+** Directory-local variables are now found in .dir-settings.el. See
+also `set-directory-project' and `define-project-bindings'.
+
** The new function `format-seconds' converts a number of seconds into a
readable string of days, hours, etc.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-19 17:07 Updated project-specific settings patch Tom Tromey
2008-05-19 18:51 ` Stefan Monnier
@ 2008-05-19 19:32 ` joakim
2008-05-19 20:11 ` Re[2]: " Eric M. Ludlam
2008-05-19 20:02 ` Chong Yidong
2 siblings, 1 reply; 25+ messages in thread
From: joakim @ 2008-05-19 19:32 UTC (permalink / raw
To: tromey; +Cc: emacs-devel
Funnily I was making a list of what features Id like in an emacs project
facility today:
Whats there already:
- JDEE, for java projects
- cedet proj facility EDE, mostly for makefile based projects
- some project dirvars implementations, such as .dir-settings.el
What I'd really like:
- find file in project, only src not .o files etc
- specify some different build commands for a project
- specify some different debug targets
What would be nice
- recursive inheritable proj defs
- use the customize gui for proj settings, like JDEE
- generic project templates that can be instantiated for c++, maven
java, plain java, etc
Tom Tromey <tromey@redhat.com> writes:
> A long while ago I was working on a patch to allow project-specific
> settings for Emacs. Here's the updated version of this patch.
>
> I tried to incorporate all the feedback on previous versions of this
> patch into the current patch. In particular:
>
> * Now a patch to files.el, not a separate file.
>
> * Reuses the hack-local-variables code to query the user about risky
> settings.
>
> * Search for directory settings is limited to a single file name,
> ".dir-settings.el".
>
> Tom
>
--
Joakim Verona
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re[2]: Project local variables & EDE (was: Updated project-specific settings patch)
2008-05-19 19:23 ` Tom Tromey
@ 2008-05-19 19:51 ` Eric M. Ludlam
2008-05-19 22:41 ` Updated project-specific settings patch Michael Olson
1 sibling, 0 replies; 25+ messages in thread
From: Eric M. Ludlam @ 2008-05-19 19:51 UTC (permalink / raw
To: Tom Tromey; +Cc: monnier, emacs-devel
>>> Tom Tromey <tromey@redhat.com> seems to think that:
>>>>>> "Stefan" == Stefan Monnier <monnier@iro.umontreal.ca> writes:
[ ... ]
>
>Stefan> One more thing: since we want to include CEDET and CEDET has its own
>Stefan> notion of "project data", we want to try and get those two things
>Stefan> to converge.
>
>Stefan> So please try and work out with zappo@gnu.org what convergence might
>Stefan> look like.
>
>I've CC'd him.
>
>I looked through CEDET a bit but project-local settings do not seem to
>be extensively documented. I think you have to use EDE to get them,
>and from what I can see EDE only supports a couple types of projects.
>
>Also, it appears that a Project.ede is needed in every directory.
Hi,
The project-local variables are supported by any EDE project type.
This includes the ones with Project.ede files, or the simpler
projects that have no save files. Of course, without a save file, the
project-local variables aren't persistent.
You are right they are not well documented. The goal of EDE when I
wrote it was not project-local variables. They are a bit of a freebie
feature in EDE.
One thing I learned since writing EDE is that I need to break the
"project" as a group of files concept apart from all the makefile
management stuff. I have a first pass in the "simple" project type
which just has a high-level project at some root directory. It needs
some work though.
The goals behind EDE are very different from projectlocal if it the
same as what I had read on emacswiki a while back. EDE uses objects
and methods to store, manipulate, and configure data. Every buffer
gets associated with an EDE project if it is in one. EDE should then
provide an API for a user facing program to navigate a project. EDE
then also provides all the makefile manipulation stuff for itself.
The API to other programs is important for other parts of CEDET
which want to have configurations specific under a particular project.
For example, ECB will draw it's dir tree rooted to some project.
Semantic will lookup header files for C++ programs within the current
project. The template manager/code-generator (SRecode) will lookup
(someday) templates changes specific to a particular project.
My suspicion is that projectlocal will satisfy a different group of
users where EDE is a bit more heavy weight. It would be easy for EDE
to identify .dir-settings.el files and automatically configure those
directories as simple EDE projects that exclude all the other
heavyweight make system features. In this case, EDE would be
duplicating some of the work of project-local. If there is interest,
I'd be happy to help make an EDE project style that uses a
.dir-settings of the same format that uses EDE constructs to do the
variable settings. The detriment to this would be that EDE will
likely be slower at runtime than the existing code, simply due to some
of the extra machinery. The benefit would be a single project-ish
system.
Tom, if you'd like to do this, I'll send you a mock-up of the EDE
code needed to merge the two to try to see if it satisfies your goal
set. Alternately, you could look in CEDET's CVS repository for
ede-simple.el which, as far as I know, does persistent project-local
variables, though I hadn't tested that since, as I mentioned above,
that wasn't my original goal.
Eric
--
Eric Ludlam: eric@siege-engine.com
Siege: www.siege-engine.com Emacs: http://cedet.sourceforge.net
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-19 17:07 Updated project-specific settings patch Tom Tromey
2008-05-19 18:51 ` Stefan Monnier
2008-05-19 19:32 ` joakim
@ 2008-05-19 20:02 ` Chong Yidong
2008-05-19 21:55 ` Tom Tromey
2 siblings, 1 reply; 25+ messages in thread
From: Chong Yidong @ 2008-05-19 20:02 UTC (permalink / raw
To: tromey; +Cc: emacs-devel
Could you also write a patch for the Emacs manual? (It doesn't have to
be ready before you check in the code, but the sooner the better.)
Thanks.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re[2]: Updated project-specific settings patch
2008-05-19 19:32 ` joakim
@ 2008-05-19 20:11 ` Eric M. Ludlam
2008-05-20 3:27 ` Eli Zaretskii
0 siblings, 1 reply; 25+ messages in thread
From: Eric M. Ludlam @ 2008-05-19 20:11 UTC (permalink / raw
To: joakim; +Cc: tromey, emacs-devel
>>> joakim@verona.se seems to think that:
>Funnily I was making a list of what features Id like in an emacs project
>facility today:
>
>Whats there already:
>- JDEE, for java projects
>- cedet proj facility EDE, mostly for makefile based projects
>- some project dirvars implementations, such as .dir-settings.el
>
>What I'd really like:
>- find file in project, only src not .o files etc
>- specify some different build commands for a project
>- specify some different debug targets
>
>What would be nice
>- recursive inheritable proj defs
>- use the customize gui for proj settings, like JDEE
EDE does all the things you mention above except it wants to also
build Makefiles. The features you list, however, are not dependent on
the Makefile build system. It would be a matter of coding a new EDE
project style that excludes the Makefile construction, and wraps up
the other concepts to simple string configurations. The machinery to
do the other stuff would be inherited.
The SRC find problem, however, gets a little dicey for particularly
large projects. I always though using locate, or something similar
would be the way to go for that.
>- generic project templates that can be instantiated for c++, maven
>java, plain java, etc
EDE doesn't do templates, though a template using application could
use EDE to identify a project, or be told of the need to do a code-gen
pass. If you mean a template to build a directory structure and
populate it with a few files, I have nothing for that.
Here is a quick summary of EDE code to help explain the above, and
perhaps my other post on this topic.
EDE uses EIEIO which implements some of CLOS. EIEIO objects can use
`customize' to allow changing slot values via custom.
EDE has a set of "project autoloads" that describe different project
types. When EDE is active, when a file is loaded, if it matches one
of the autoloads, the actual project style is loaded, and that project
style then operates on that set of files.
Code-wise, there is a class hierarchy, starting with the autoload
projects.
Inheriting from there is the base EDE project and target classes.
These define the behavior of all EDE projects and targets, and the EDE
user interface (menu, compile, debug, find, configure) all operates
against that interface.
Inheriting from the base EDE projects are targets are:
project-am - Sees Makefile.am, and operates directly from that. No
need for anything else. You can "add" a file to a target, and it will
edit Makefile.am for you.
ede-proj - This is the project type that creates Project.ede files
folks see and talk about. It creates a new Makefile or Makefile.am
whenever you customize the project object.
ede-simple - A first cut at a simplified not too smart project
ede-cpp-root - A c++ specific project designed for finding C/C++ files
in a project. No persistence.
Each project type then implements whichever aspects of the UI behavior
it wants.
Eric
--
Eric Ludlam: eric@siege-engine.com
Siege: www.siege-engine.com Emacs: http://cedet.sourceforge.net
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-19 18:51 ` Stefan Monnier
2008-05-19 18:55 ` Miles Bader
2008-05-19 19:23 ` Tom Tromey
@ 2008-05-19 20:29 ` David Kastrup
2008-05-19 21:57 ` Stefan Monnier
2 siblings, 1 reply; 25+ messages in thread
From: David Kastrup @ 2008-05-19 20:29 UTC (permalink / raw
To: Stefan Monnier; +Cc: tromey, emacs-devel
Stefan Monnier <monnier@iro.umontreal.ca> writes:
> Better use the same buffer name *Local Variables*.
>
>> + ((file-exists-p (concat dir ".dir-settings.el"))
>> + (setq result (concat dir ".dir-settings.el")))
>
> In most cases, we'd prefer (expand-file-name ".dir-settings.el" dir)
> over the use of `concat'. The only exception I know is when `dir' is
> relative and it's important for the result also be relative.
If the file name or directory has been read directly from disk and thus
may contain "~" or "~USER" as valid literal components, you don't want
to have those expanded.
--
David Kastrup, Kriemhildstr. 15, 44793 Bochum
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-19 20:02 ` Chong Yidong
@ 2008-05-19 21:55 ` Tom Tromey
2008-05-20 14:02 ` Chong Yidong
0 siblings, 1 reply; 25+ messages in thread
From: Tom Tromey @ 2008-05-19 21:55 UTC (permalink / raw
To: Chong Yidong; +Cc: emacs-devel
>>>>> "Chong" == Chong Yidong <cyd@stupidchicken.com> writes:
Chong> Could you also write a patch for the Emacs manual? (It doesn't have to
Chong> be ready before you check in the code, but the sooner the better.)
The patch included a change to doc/emacs/custom.texi.
Is there something else I should do?
Tom
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-19 20:29 ` David Kastrup
@ 2008-05-19 21:57 ` Stefan Monnier
0 siblings, 0 replies; 25+ messages in thread
From: Stefan Monnier @ 2008-05-19 21:57 UTC (permalink / raw
To: David Kastrup; +Cc: tromey, emacs-devel
>> Better use the same buffer name *Local Variables*.
>>
>>> + ((file-exists-p (concat dir ".dir-settings.el"))
>>> + (setq result (concat dir ".dir-settings.el")))
>>
>> In most cases, we'd prefer (expand-file-name ".dir-settings.el" dir)
>> over the use of `concat'. The only exception I know is when `dir' is
>> relative and it's important for the result also be relative.
> If the file name or directory has been read directly from disk and thus
> may contain "~" or "~USER" as valid literal components, you don't want
> to have those expanded.
That simply depends on what the result is used for.
In 99% of the cases in Emacs, the difference between the two
doesn't matter.
Stefan
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-19 19:23 ` Tom Tromey
2008-05-19 19:51 ` Re[2]: Project local variables & EDE (was: Updated project-specific settings patch) Eric M. Ludlam
@ 2008-05-19 22:41 ` Michael Olson
2008-05-20 0:04 ` Stefan Monnier
2008-05-20 0:14 ` Miles Bader
1 sibling, 2 replies; 25+ messages in thread
From: Michael Olson @ 2008-05-19 22:41 UTC (permalink / raw
To: emacs-devel
[-- Attachment #1: Type: text/plain, Size: 815 bytes --]
Tom Tromey <tromey@redhat.com> writes:
> Stefan> In most cases, we'd prefer (expand-file-name
> Stefan> ".dir-settings.el" dir) over the use of `concat'. The only
> Stefan> exception I know is when `dir' is relative and it's important
> Stefan> for the result also be relative.
>
> No problem.
>
> New patch appended.
I've applied this patch. I modified it to go back to the old behavior
of using concat, however, because there were some objections to using
expand-file-name here, and I don't like calling expand-file-name where
it's not really needed.
--
| Michael Olson | FSF Associate Member #652 |
| http://mwolson.org/ | Hobbies: Lisp, HCoop |
| Projects: Emacs, Muse, ERC, EMMS, ErBot, DVC, Planner |
`-------------------------------------------------------'
[-- Attachment #2: Type: application/pgp-signature, Size: 188 bytes --]
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-19 22:41 ` Updated project-specific settings patch Michael Olson
@ 2008-05-20 0:04 ` Stefan Monnier
2008-05-20 3:18 ` Michael Olson
2008-05-20 0:14 ` Miles Bader
1 sibling, 1 reply; 25+ messages in thread
From: Stefan Monnier @ 2008-05-20 0:04 UTC (permalink / raw
To: Michael Olson; +Cc: emacs-devel
Stefan> In most cases, we'd prefer (expand-file-name
Stefan> ".dir-settings.el" dir) over the use of `concat'. The only
Stefan> exception I know is when `dir' is relative and it's important
Stefan> for the result also be relative.
>>
>> No problem.
>>
>> New patch appended.
> I've applied this patch. I modified it to go back to the old behavior
> of using concat, however, because there were some objections to using
> expand-file-name here, and I don't like calling expand-file-name where
> it's not really needed.
Please don't, it's the other way 'round: we should only use concat to
build file names when expand-file-name is wrong. In doubt, use
expand-file-name.
Stefan
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-19 22:41 ` Updated project-specific settings patch Michael Olson
2008-05-20 0:04 ` Stefan Monnier
@ 2008-05-20 0:14 ` Miles Bader
2008-05-20 7:19 ` David Kastrup
1 sibling, 1 reply; 25+ messages in thread
From: Miles Bader @ 2008-05-20 0:14 UTC (permalink / raw
To: Michael Olson; +Cc: emacs-devel
Michael Olson <mwolson@gnu.org> writes:
> I've applied this patch. I modified it to go back to the old behavior
> of using concat, however, because there were some objections to using
> expand-file-name here, and I don't like calling expand-file-name where
> it's not really needed.
"Some objections"?
Concat is generally not the right thing for handling filenames; if there
are concrete problems with expand-file-name, they should be addressed.
-Miles
--
Accord, n. Harmony.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-19 19:00 ` Tom Tromey
@ 2008-05-20 2:35 ` Miles Bader
0 siblings, 0 replies; 25+ messages in thread
From: Miles Bader @ 2008-05-20 2:35 UTC (permalink / raw
To: Tom Tromey; +Cc: Stefan Monnier, emacs-devel
Tom Tromey <tromey@redhat.com> writes:
> Miles> Should this be called ".emacs-settings.el"? I guess the ".el" is
> Miles> supposed to imply that, but it might not be so obvious to everybody...
>
> There were a couple long threads about the naming. My recollection is
> that RMS required that there be a single name and that it not include
> the word "emacs". Given those constraints, this is what I picked.
I see; if it's already been discussed, I probably can't add
anything new, so never mind... :-)
-Miles
--
"Suppose we've chosen the wrong god. Every time we go to church we're
just making him madder and madder." -- Homer Simpson
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-20 0:04 ` Stefan Monnier
@ 2008-05-20 3:18 ` Michael Olson
0 siblings, 0 replies; 25+ messages in thread
From: Michael Olson @ 2008-05-20 3:18 UTC (permalink / raw
To: emacs-devel
[-- Attachment #1: Type: text/plain, Size: 774 bytes --]
Stefan Monnier <monnier@IRO.UMontreal.CA> writes:
>> I've applied this patch. I modified it to go back to the old
>> behavior of using concat, however, because there were some objections
>> to using expand-file-name here, and I don't like calling
>> expand-file-name where it's not really needed.
>
> Please don't, it's the other way 'round: we should only use concat to
> build file names when expand-file-name is wrong. In doubt, use
> expand-file-name.
OK, I've checked in the change for using expand-file-name.
--
| Michael Olson | FSF Associate Member #652 |
| http://mwolson.org/ | Hobbies: Lisp, HCoop |
| Projects: Emacs, Muse, ERC, EMMS, ErBot, DVC, Planner |
`-------------------------------------------------------'
[-- Attachment #2: Type: application/pgp-signature, Size: 188 bytes --]
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-19 20:11 ` Re[2]: " Eric M. Ludlam
@ 2008-05-20 3:27 ` Eli Zaretskii
0 siblings, 0 replies; 25+ messages in thread
From: Eli Zaretskii @ 2008-05-20 3:27 UTC (permalink / raw
To: Eric M. Ludlam; +Cc: tromey, joakim, emacs-devel
> Date: Mon, 19 May 2008 16:11:38 -0400
> From: "Eric M. Ludlam" <eric@siege-engine.com>
> Cc: tromey@redhat.com, emacs-devel@gnu.org
>
> The SRC find problem, however, gets a little dicey for particularly
> large projects. I always though using locate, or something similar
> would be the way to go for that.
ID-Utils is a GNU package that specifically targets that niche. I
find it invaluable in this role (it comes with a simple grep-style
interface to Emacs).
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-20 0:14 ` Miles Bader
@ 2008-05-20 7:19 ` David Kastrup
0 siblings, 0 replies; 25+ messages in thread
From: David Kastrup @ 2008-05-20 7:19 UTC (permalink / raw
To: Miles Bader; +Cc: Michael Olson, emacs-devel
Miles Bader <miles@gnu.org> writes:
> Michael Olson <mwolson@gnu.org> writes:
>> I've applied this patch. I modified it to go back to the old behavior
>> of using concat, however, because there were some objections to using
>> expand-file-name here, and I don't like calling expand-file-name where
>> it's not really needed.
>
> "Some objections"?
>
> Concat is generally not the right thing for handling filenames;
Why?
> if there are concrete problems with expand-file-name, they should be
> addressed.
I already said that expand-file does ~ expansion which may not be
wanted.
And we have
file-name-directory is a built-in function in `C source code'.
(file-name-directory FILENAME)
Return the directory component in file name FILENAME.
Return nil if FILENAME does not include a directory.
Otherwise return a directory name.
Given a Unix syntax file name, returns a string ending in slash;
on VMS, perhaps instead a string ending in `:', `]' or `>'.
[back]
"directory component" very much sounds like it is a direct substring.
And
file-name-nondirectory is a built-in function in `C source code'.
(file-name-nondirectory FILENAME)
Return file name FILENAME sans its directory.
For example, in a Unix-syntax file name,
this is everything after the last slash,
or the entire name if it contains no slash.
[back]
also very much sounds like it is a substring.
So why wouldn't one expect
(string= f (concat (file-name-directory f) (file-name-nondirectory f)))
to hold?
--
David Kastrup, Kriemhildstr. 15, 44793 Bochum
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-19 21:55 ` Tom Tromey
@ 2008-05-20 14:02 ` Chong Yidong
2008-05-20 18:45 ` Stefan Monnier
2008-05-21 19:49 ` Tom Tromey
0 siblings, 2 replies; 25+ messages in thread
From: Chong Yidong @ 2008-05-20 14:02 UTC (permalink / raw
To: Tom Tromey; +Cc: emacs-devel
Tom Tromey <tromey@redhat.com> writes:
>>>>>> "Chong" == Chong Yidong <cyd@stupidchicken.com> writes:
>
> Chong> Could you also write a patch for the Emacs manual? (It doesn't have to
> Chong> be ready before you check in the code, but the sooner the better.)
>
> The patch included a change to doc/emacs/custom.texi.
I missed that part of your patch. Sorry for the noise. I've read it
now, and have a couple of minor comments.
Emacs provides a way to specify local variable values per-directory.
This can be done one of two ways.
This is confusingly worded: is there "a way" or "two ways"? I suggest
changing this to:
Directory variables are file-local variables that apply to all the
files within a directory. You can define directory variables in two
ways.
Also,
Unsafe directory-local variables are handled in the same way as
unsafe file-local variables.
You need to be more specific about what happens when the user tries to
define unsafe directory-local variables.
On another note: does your patch include a enable-directory-variables
option, analogous to enable-local-variables, that allows the user to
turn off directory variable scanning? If not, maybe should.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-20 14:02 ` Chong Yidong
@ 2008-05-20 18:45 ` Stefan Monnier
2008-05-21 0:04 ` Miles Bader
2008-05-21 19:49 ` Tom Tromey
1 sibling, 1 reply; 25+ messages in thread
From: Stefan Monnier @ 2008-05-20 18:45 UTC (permalink / raw
To: Chong Yidong; +Cc: Tom Tromey, emacs-devel
> Directory variables are file-local variables...
That doesn't sound right either.
> On another note: does your patch include a enable-directory-variables
> option, analogous to enable-local-variables, that allows the user to
> turn off directory variable scanning? If not, maybe should.
I don't see any particular need for that. We can add it later, if it
turns out to be a common request.
Stefan
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-20 18:45 ` Stefan Monnier
@ 2008-05-21 0:04 ` Miles Bader
0 siblings, 0 replies; 25+ messages in thread
From: Miles Bader @ 2008-05-21 0:04 UTC (permalink / raw
To: Stefan Monnier; +Cc: Tom Tromey, Chong Yidong, emacs-devel
Stefan Monnier <monnier@iro.umontreal.ca> writes:
>> Directory variables are file-local variables...
>
> That doesn't sound right either.
How about using the original wording, but replacing "a way"
with "a mechanism"?
-Miles
--
"Suppose we've chosen the wrong god. Every time we go to church we're
just making him madder and madder." -- Homer Simpson
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-20 14:02 ` Chong Yidong
2008-05-20 18:45 ` Stefan Monnier
@ 2008-05-21 19:49 ` Tom Tromey
2008-05-22 3:46 ` Chong Yidong
1 sibling, 1 reply; 25+ messages in thread
From: Tom Tromey @ 2008-05-21 19:49 UTC (permalink / raw
To: Chong Yidong; +Cc: emacs-devel
Chong> I missed that part of your patch. Sorry for the noise. I've read it
Chong> now, and have a couple of minor comments.
Thanks. What do you think of the appended patch?
I tried to take your comments into account, as well as Miles'.
Chong> On another note: does your patch include a enable-directory-variables
Chong> option, analogous to enable-local-variables, that allows the user to
Chong> turn off directory variable scanning? If not, maybe should.
Based on Stefan's response, I don't plan to write this. However, if
you feel strongly about it, let me know and I will write a patch.
Tom
2008-05-21 Tom Tromey <tromey@redhat.com>
* custom.texi (Directory Variables): Grammar fix. Link to Safe File
Variables node.
Index: custom.texi
===================================================================
RCS file: /sources/emacs/emacs/doc/emacs/custom.texi,v
retrieving revision 1.10
diff -u -c -r1.10 custom.texi
cvs diff: conflicting specifications of output style
*** custom.texi 19 May 2008 22:36:08 -0000 1.10
--- custom.texi 21 May 2008 19:43:26 -0000
***************
*** 1268,1274 ****
@cindex local variables in directories
@cindex directory local variables
! Emacs provides a way to specify local variable values per-directory.
This can be done one of two ways.
The first approach is to put a special file, named
--- 1268,1274 ----
@cindex local variables in directories
@cindex directory local variables
! Emacs provides a mechanism to specify local variable values per-directory.
This can be done one of two ways.
The first approach is to put a special file, named
***************
*** 1320,1326 ****
@end example
Unsafe directory-local variables are handled in the same way as
! unsafe file-local variables.
@node Key Bindings
@section Customizing Key Bindings
--- 1320,1326 ----
@end example
Unsafe directory-local variables are handled in the same way as
! unsafe file-local variables (@pxref{Safe File Variables}).
@node Key Bindings
@section Customizing Key Bindings
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-21 19:49 ` Tom Tromey
@ 2008-05-22 3:46 ` Chong Yidong
2008-05-23 6:31 ` Michael Olson
0 siblings, 1 reply; 25+ messages in thread
From: Chong Yidong @ 2008-05-22 3:46 UTC (permalink / raw
To: Tom Tromey; +Cc: emacs-devel
Tom Tromey <tromey@redhat.com> writes:
> Thanks. What do you think of the appended patch?
I think it's OK.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Updated project-specific settings patch
2008-05-22 3:46 ` Chong Yidong
@ 2008-05-23 6:31 ` Michael Olson
0 siblings, 0 replies; 25+ messages in thread
From: Michael Olson @ 2008-05-23 6:31 UTC (permalink / raw
To: emacs-devel
[-- Attachment #1: Type: text/plain, Size: 425 bytes --]
Chong Yidong <cyd@stupidchicken.com> writes:
> Tom Tromey <tromey@redhat.com> writes:
>
>> Thanks. What do you think of the appended patch?
>
> I think it's OK.
Applied.
--
| Michael Olson | FSF Associate Member #652 |
| http://mwolson.org/ | Hobbies: Lisp, HCoop |
| Projects: Emacs, Muse, ERC, EMMS, ErBot, DVC, Planner |
`-------------------------------------------------------'
[-- Attachment #2: Type: application/pgp-signature, Size: 188 bytes --]
^ permalink raw reply [flat|nested] 25+ messages in thread
end of thread, other threads:[~2008-05-23 6:31 UTC | newest]
Thread overview: 25+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-05-19 17:07 Updated project-specific settings patch Tom Tromey
2008-05-19 18:51 ` Stefan Monnier
2008-05-19 18:55 ` Miles Bader
2008-05-19 19:00 ` Tom Tromey
2008-05-20 2:35 ` Miles Bader
2008-05-19 19:23 ` Tom Tromey
2008-05-19 19:51 ` Re[2]: Project local variables & EDE (was: Updated project-specific settings patch) Eric M. Ludlam
2008-05-19 22:41 ` Updated project-specific settings patch Michael Olson
2008-05-20 0:04 ` Stefan Monnier
2008-05-20 3:18 ` Michael Olson
2008-05-20 0:14 ` Miles Bader
2008-05-20 7:19 ` David Kastrup
2008-05-19 20:29 ` David Kastrup
2008-05-19 21:57 ` Stefan Monnier
2008-05-19 19:32 ` joakim
2008-05-19 20:11 ` Re[2]: " Eric M. Ludlam
2008-05-20 3:27 ` Eli Zaretskii
2008-05-19 20:02 ` Chong Yidong
2008-05-19 21:55 ` Tom Tromey
2008-05-20 14:02 ` Chong Yidong
2008-05-20 18:45 ` Stefan Monnier
2008-05-21 0:04 ` Miles Bader
2008-05-21 19:49 ` Tom Tromey
2008-05-22 3:46 ` Chong Yidong
2008-05-23 6:31 ` Michael Olson
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.