unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* status of dir-vars or dir-locals inclusion in emacs?
@ 2007-07-03 13:48 joakim
  2007-07-04  0:25 ` Tom Tromey
                   ` (2 more replies)
  0 siblings, 3 replies; 28+ messages in thread
From: joakim @ 2007-07-03 13:48 UTC (permalink / raw)
  To: emacs-devel

There was discussion some time ago about including either
dir-locals.el or dir-vars.el.

- What is the status of this?  Which one will be included?

- Will it be  possible to specify things like which compiler command to use?

I normaly dont use file local variables but I do use project specific
settings as provided by the JDEE(java development environment for
emacs), and would like some more general project setting feature in
emacs.



-- 
Joakim Verona

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-07-03 13:48 status of dir-vars or dir-locals inclusion in emacs? joakim
@ 2007-07-04  0:25 ` Tom Tromey
  2007-07-10 22:01   ` Richard Stallman
  2007-07-04 17:04 ` Vagn Johansen
  2007-07-05  1:29 ` Richard Stallman
  2 siblings, 1 reply; 28+ messages in thread
From: Tom Tromey @ 2007-07-04  0:25 UTC (permalink / raw)
  To: joakim; +Cc: emacs-devel

>>>>> "Joakim" == Joakim Verona <joakim@verona.se> writes:

Joakim> There was discussion some time ago about including either
Joakim> dir-locals.el or dir-vars.el.

Joakim> - What is the status of this?  Which one will be included?

One thing I don't like about dirvars is that, AFAICS, it only allows a
single setting of a variable for a project -- there's no way to have
different settings depending on the major mode.

Here's some code I've been playing with that does the same kind of
thing, in a slightly different way.  You can define a project class
and have it define settings and then define different directories as
instances of a defined class, or you can have a file in the top-level
directory of a project.

I do like how dirvars by default doesn't search upward on remote
directories.  I wish I'd thought of that :-).

Tom

;;; project.el --- per-project settings

;; Copyright (C) 2007 Tom Tromey <tromey@redhat.com>

;; Author: Tom Tromey <tromey@redhat.com>
;; Created: 27 Apr 2007
;; Version: 0.1
;; Keywords: tools

;; This file is not (yet) part of GNU Emacs.
;; However, it is distributed under the same license.

;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.

;;; Commentary:

;; This package makes it easy to set variables on a per-mode basis for
;; an entire project.  This is much simpler than editing the local
;; variables section of every file in the project.  Settings can be
;; checked in to the project's version control and they will
;; automatically be found and used by Emacs.

;; FIXME: elpa instructions
;; To set up:
;;   (add-hook 'find-file-hooks #'project-find-file)

;; A project class can be defined manually using
;; `project-define-class'.  Then a directory can be associated with a
;; given class using `project-define-instance'.  This indirection lets
;; you easily share settings between multiple instances of a project,
;; for instance if you have multiple branches checked out.

;; The alist argument to `project-define-class' is an alist with a
;; special structure, defined below.

;; When a file is loaded, project.el will search up the directory
;; hierarchy.  If it finds a directory that was registered with
;; `project-define-instance', it will use the corresponding project
;; class.

;; Otherwise, if a directory contains a file `settings.el' or
;; `.settings.el', then the contents of that file are used as an alist
;; (of the type `project-define-class' takes).  In this case, the
;; alist is first scrubbed of risky variable settings.

;; The alist referred to above is used to set local variables.  The
;; car of an entry in the alist is one of three things: the major mode
;; (a symbol); `nil', which lists variables applied to every file in
;; the project; or a string, which is a subdirectory of the project.

;; If the car is a mode name or `nil', the cdr is an alist mapping
;; variable names to variable values.

;; If the car is a string, the cdr is another alist of the form above.

;; Here's an example for GCC:

;; (project-define-class 'gcc
;;   '((nil . ((indent-tabs-mode . t)
;; 	    (tab-width . 8)
;; 	    (fill-column . 80)))
;;     (c-mode . ((style . "GNU")
;; 	       (new-file-skeleton . (blah))))
;;     (java-mode . ((style . "GNU")))
;;     ("libjava/classpath"
;;      . ((nil . ((change-log-default-name . "ChangeLog.gcj")))))
;; 
;;     ))

;; Note that in general you should only set project-related variables
;; in a settings.el file that is checked in.  Things like key bindings
;; (or electric keys for C mode) should probably not be set by the
;; project.

;;; ToDo:

;; - need a way to add to auto-mode-alist per-project
;;   e.g., semantic wants GCC's .def files to be c-mode
;; - should cache the mod time of settings file and then reload it
;;   or at least offer user a way to invalidate cache
;; - should let the user augment the project settings with personal ones

;;; Code:

(defvar project-class-alist '()
  "Alist mapping project class names (symbols) to project variable alists.")

(defvar project-directory-alist '()
  "Alist mapping project directory roots to project classes.")

(defsubst project-get-alist (class)
  "Return the project variable alist for project CLASS."
  (cdr (assq class project-class-alist)))

(defun project-set-variables-from-alist (mode-alist)
  "Apply local variable settings from MODE-ALIST."
  (mapc (lambda (pair)
	  (let ((variable (car pair))
		(value (cdr pair)))
	    (make-local-variable variable)
	    (set variable value)))
	mode-alist))

(defun project-set-variables (class root)
  "Set variables for the current buffer for the given project class.
CLASS is the project's class, a symbol.
ROOT is the project's root directory, a string.
This applies local variable settings in order from most generic
to most specific."
  (let* ((alist (project-get-alist class))
	 (subdir (substring (buffer-file-name) 0 (length root))))
    ;; First use the 'nil' key to get generic variables.
    (project-set-variables-from-alist (cdr (assq nil alist)))
    ;; Now apply the mode-specific variables.
    (project-set-variables-from-alist (cdr (assq major-mode alist)))
    ;; Look for subdirectory matches in the class alist and apply
    ;; based on those.
    (mapc (lambda (elt)
	    (and (stringp (car elt))
		 (string= (car elt) (substring (buffer-file-name) 0
					       (length (car elt))))
		 (progn
		   ;; Again both generic and mode-specific.
		   (project-set-variables-from-alist
		    (cdr (assq nil alist)))
		   (project-set-variables-from-alist
		    (cdr (assq major-mode alist))))))
	  alist)
    ;; Special case C and derived modes.  Note that CC-based modes
    ;; don't work with derived-mode-p.  FIXME: this is arguably an
    ;; Emacs bug.  Perhaps we should be running
    ;; hack-local-variables-hook here instead?
    (and (boundp 'c-buffer-is-cc-mode)
	 c-buffer-is-cc-mode
	 (c-postprocess-file-styles))))

;;;###autoload
(defun project-define-instance (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."
  (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)))
  (setq project-directory-alist
	(cons (cons directory class)
	      project-directory-alist)))

;;;###autoload
(defun project-define-class (class alist)
  "Map the project type CLASS to an alist of variable settings.
CLASS is the project class, a symbol.
ALIST is an alist that maps major modes to sub-alists.
Each sub-alist maps variable names to values.

Note that this does not filter risky variables.  This function
is intended for use by trusted code only."
  (let ((elt (assq class project-class-alist)))
    (if elt
	(setcdr elt alist)
      (setq project-class-alist
	    (cons (cons class alist)
		  project-class-alist)))))

;; There's a few ways we could do this.  We could use VC (with a VC
;; extension) and look for the root directory.  Or we could chain
;; settings files.  For now we choose a simple approach and let the
;; project maintainers be smart.
(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 "settings.el"))
	(setq result (concat dir "settings.el")))
       ((file-exists-p (concat dir ".settings.el"))
	(setq result (concat dir ".settings.el")))
       (t
	(setq dir (file-name-directory (directory-file-name dir))))))
    result))

;; Taken from Emacs 22.
(defun project-safe-local-variable-p (sym val)
  "Non-nil if SYM is safe as a file-local variable with value VAL.
It is safe if any of these conditions are met:

 * There is a matching entry (SYM . VAL) in the
   `safe-local-variable-values' user option.

 * The `safe-local-variable' property of SYM is a function that
   evaluates to a non-nil value with VAL as an argument."
  (or (member (cons sym val) safe-local-variable-values)
      (let ((safep (get sym 'safe-local-variable)))
        (and (functionp safep) (funcall safep val)))))

(unless (fboundp 'safe-local-variable-p)
  (fset 'safe-local-variable-p 'project-safe-local-variable-p))

(defun project-filter-risky-variables (alist)
  "Filter risky variables from the project settings ALIST.
This knows the expected structure of a project settings alist.
Actually this filters unsafe variables."
  (mapc (lambda (elt)
	  (let ((sub-alist (cdr elt)))
	    (if (stringp (car sub-alist))
		;; A string element maps to a secondary alist.
		(setcdr sub-alist
			(project-filter-risky-variables (cdr sub-alist)))
	      ;; Remove unsafe variables by setting their cars to nil.
	      ;; FIXME: or look only at risky-local-variable-p?
	      (mapc (lambda (sub-elt)
		      (unless (safe-local-variable-p (car sub-elt)
						     (cdr sub-elt))
			(setcar sub-elt nil)))
		    sub-alist)
	      ;; Now remove all the deleted risky variables.
	      (setcdr elt (assq-delete-all nil sub-alist)))))
	alist)
  alist)

(defun project-define-from-project-file (settings-file)
  "Load a settings file and register a new project class and instance.
The class name is the same as the directory in which the settings file
was found.  The settings have risky local variables filtered out."
  (with-temp-buffer
    (insert-file-contents settings-file)
    (let* ((dir-name (file-name-directory settings-file))
	   (class-name (intern dir-name))
	   (alist (project-filter-risky-variables (read (current-buffer)))))
      (project-define-class class-name alist)
      (project-define-instance dir-name class-name)
      class-name)))

;; Put this on find-file-hooks.
;;;###autoload
(defun project-find-file ()
  "Set local variables in a buffer based on project settings."
  (when (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 class (project-define-from-project-file settings))
	(setq root-dir (file-name-directory (buffer-file-name))))
       ((consp settings)
	(setq root-dir (car settings))
	(setq class (cdr settings))))
      (when class
	(make-local-variable 'project-class)
	(setq project-class class)
	(project-set-variables class root-dir)))))

;;; project.el ends here

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-07-03 13:48 status of dir-vars or dir-locals inclusion in emacs? joakim
  2007-07-04  0:25 ` Tom Tromey
@ 2007-07-04 17:04 ` Vagn Johansen
  2007-07-05  1:29 ` Richard Stallman
  2 siblings, 0 replies; 28+ messages in thread
From: Vagn Johansen @ 2007-07-04 17:04 UTC (permalink / raw)
  To: emacs-devel

joakim@verona.se writes:

> There was discussion some time ago about including either
> dir-locals.el or dir-vars.el.
>
> - What is the status of this?  Which one will be included?

After upgrading to Emacs 22.1 I had some problems with partially font
locked buffers. I tracked it down to dirvars.el.

I wrote about it on EmacsWiki a week ago
(http://www.emacswiki.org/cgi-bin/wiki/DirVarsPackage) and added a link
to this relevant discussion
http://osdir.com/ml/emacs.sources/2006-06/msg00018.html

Note: Just yesterday I had to stop using dirvars 1.3 and switch to
dir-locals.el because the issue persisted.

-- 
Vagn Johansen

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-07-03 13:48 status of dir-vars or dir-locals inclusion in emacs? joakim
  2007-07-04  0:25 ` Tom Tromey
  2007-07-04 17:04 ` Vagn Johansen
@ 2007-07-05  1:29 ` Richard Stallman
  2 siblings, 0 replies; 28+ messages in thread
From: Richard Stallman @ 2007-07-05  1:29 UTC (permalink / raw)
  To: joakim; +Cc: emacs-devel

    There was discussion some time ago about including either
    dir-locals.el or dir-vars.el.

    - What is the status of this?  Which one will be included?

I don't remember the past discussion.  It would be useful to look
it up and see if we determined which of these was cleaner.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-07-04  0:25 ` Tom Tromey
@ 2007-07-10 22:01   ` Richard Stallman
  2007-07-29 22:59     ` Tom Tromey
  0 siblings, 1 reply; 28+ messages in thread
From: Richard Stallman @ 2007-07-10 22:01 UTC (permalink / raw)
  To: tromey; +Cc: joakim, emacs-devel

project.el looks like just the right thing.
Could you write changes to etc/NEWS and the Emacs Manual
to describe it?

I think a few small changes should be made.

* Rename `project-define-class' to `define-project-bindings'.

* Rename `project-define-instance' to `set-directory-project'.

* Rename `project-set-variables' to `project-install-bindings'.
Likewise other names derived from that one.

    ;; - need a way to add to auto-mode-alist per-project
    ;;   e.g., semantic wants GCC's .def files to be c-mode

That seems secondary -- it is not hard to make files specify
their major modes.

    ;; - should cache the mod time of settings file and then reload it
    ;;   or at least offer user a way to invalidate cache

Does that mean, in case the project settings change, it should update
the bindings in the relevant buffers?  It should be easy to store in
the alist a cons cell that was made by `define-project-bindings', and
copy that into a buffer-local variable.  Then by comparison you can
tell whether the project's settings have changed since they were
installed into the current buffer.

    ;; - should let the user augment the project settings with personal ones

Isn't this what `project-define-class' does?
How would this be different from that?

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-07-10 22:01   ` Richard Stallman
@ 2007-07-29 22:59     ` Tom Tromey
  2007-07-30 14:45       ` Stefan Monnier
  2007-07-31 17:05       ` Richard Stallman
  0 siblings, 2 replies; 28+ messages in thread
From: Tom Tromey @ 2007-07-29 22:59 UTC (permalink / raw)
  To: rms; +Cc: joakim, emacs-devel

>>>>> "rms" == Richard Stallman <rms@gnu.org> writes:

rms> project.el looks like just the right thing.
rms> Could you write changes to etc/NEWS and the Emacs Manual
rms> to describe it?

Sorry for the delay on this.  I've been occupied elsewhere.

I haven't written the patch to Emacs yet but I am sending the updated
project.el.  I'll try to write the documentation patch soon.

I did want to ask whether this is something that should be enabled by
default.  It would be more useful that way, because Emacs would "just
work" to find settings for projects using this feature.

>     ;; - need a way to add to auto-mode-alist per-project
>     ;;   e.g., semantic wants GCC's .def files to be c-mode

rms> That seems secondary -- it is not hard to make files specify
rms> their major modes.

Yeah, good point.  I made a note.

>     ;; - should cache the mod time of settings file and then reload it
>     ;;   or at least offer user a way to invalidate cache

rms> Does that mean, in case the project settings change, it should update
rms> the bindings in the relevant buffers?

This is an interesting idea.  I was thinking of something simpler,
namely that after an update the new project settings should be picked
up for new buffers.

>     ;; - should let the user augment the project settings with personal ones

rms> Isn't this what `project-define-class' does?
rms> How would this be different from that?

Right now there are 2 ways to use project.el.

First, you can create a "settings.el" or ".settings.el" file in a
project's top-level directory.  This file holds a sexp that looks like
the argument to define-project-bindings.  The idea behind this mode of
operation is that you are working on a project that is friendly to
Emacs-using developers, so the Emacs settings are checked in with the
other source files.

The other mode is to explicitly define a project class (say in your
.emacs) using define-project-bindings, and then explicitly tell Emacs
that certain directories hold instances of that project.


The first mode here is only appropriate for shared settings -- things
like indentation settings and the like.  It would not be appropriate
for anything that is per-user -- say, if you wanted to set
compile-history on a per-project basis.

I'm not sure how useful this feature would be.  It depends on how many
settings like this there are.  Most of the ones I tend to use are
transient -- though of course maybe they wouldn't be if I had a
convenient way to save them.

Tom

;;; project.el --- per-project settings

;; Copyright (C) 2007 Tom Tromey <tromey@redhat.com>

;; Author: Tom Tromey <tromey@redhat.com>
;; Created: 27 Apr 2007
;; Version: 0.2
;; Keywords: tools

;; This file is not (yet) part of GNU Emacs.
;; However, it is distributed under the same license.

;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.

;;; Commentary:

;; This package makes it easy to set variables on a per-mode basis for
;; an entire project.  This is much simpler than editing the local
;; variables section of every file in the project.  Settings can be
;; checked in to the project's version control and they will
;; automatically be found and used by Emacs.

;; To set up:
;;   (add-hook 'find-file-hooks #'project-find-file)

;; A project class can be defined manually using
;; `define-project-bindings'.  Then a directory can be associated with a
;; given class using `set-directory-project'.  This indirection lets
;; you easily share settings between multiple instances of a project,
;; for instance if you have multiple branches checked out.

;; The alist argument to `define-project-bindings' is an alist with a
;; special structure, defined below.

;; When a file is loaded, project.el will search up the directory
;; hierarchy.  If it finds a directory that was registered with
;; `set-directory-project', it will use the corresponding project
;; class.

;; Otherwise, if a directory contains a file `settings.el' or
;; `.settings.el', then the contents of that file are used as an alist
;; (of the type `define-project-bindings' takes).  In this case, the
;; alist is first scrubbed of risky variable settings.

;; The alist referred to above is used to set local variables.  The
;; car of an entry in the alist is one of three things: the major mode
;; (a symbol); `nil', which lists variables applied to every file in
;; the project; or a string, which is a subdirectory of the project.

;; If the car is a mode name or `nil', the cdr is an alist mapping
;; variable names to variable values.

;; If the car is a string, the cdr is another alist of the form above.

;; Here's an example for GCC:

;; (define-project-bindings 'gcc
;;   '((nil . ((indent-tabs-mode . t)
;; 	    (tab-width . 8)
;; 	    (fill-column . 80)))
;;     (c-mode . ((style . "GNU")
;; 	       (new-file-skeleton . (blah))))
;;     (java-mode . ((style . "GNU")))
;;     ("libjava/classpath"
;;      . ((nil . ((change-log-default-name . "ChangeLog.gcj")))))
;; 
;;     ))

;; Note that in general you should only set project-related variables
;; in a settings.el file that is checked in.  Things like key bindings
;; (or electric keys for C mode) should probably not be set by the
;; project.

;;; ToDo:

;; - need a way to add to auto-mode-alist per-project
;;   e.g., semantic wants GCC's .def files to be c-mode
;;   (RMS points out that this is also easily done by adding comments
;;   to the particular files.)
;; - should cache the mod time of settings file and then reload it
;;   or at least offer user a way to invalidate cache
;; - should let the user augment the project settings with personal ones
;; - per-project and per-mode new file skeletons
;; - maybe an easy way to integrate with customize?
;; - make it easy to save per-project user settings like build
;;   commands, gdb commands, etc
;; - let user close a project and close all associated buffers; then
;;   reopen the project and have the buffers open?
;; - any role for bugzilla URLs, patch address and formatting?
;; - got a request to emulate Eclipse's S-C-r binding; maybe integrate
;;   with iswitch-fc or filename cache

;;; Code:

(defvar project-class-alist '()
  "Alist mapping project class names (symbols) to project variable alists.")

(defvar project-directory-alist '()
  "Alist mapping project directory roots to project classes.")

(defsubst project-get-alist (class)
  "Return the project variable alist for project CLASS."
  (cdr (assq class project-class-alist)))

(defun project-install-bindings-from-alist (mode-alist)
  "Apply local variable settings from MODE-ALIST."
  (mapc (lambda (pair)
	  (let ((variable (car pair))
		(value (cdr pair)))
	    (make-local-variable variable)
	    (set variable value)))
	mode-alist))

(defun project-install-bindings (class root)
  "Set variables for the current buffer for the given project class.
CLASS is the project's class, a symbol.
ROOT is the project's root directory, a string.
This applies local variable settings in order from most generic
to most specific."
  (let* ((alist (project-get-alist class))
	 (subdir (substring (buffer-file-name) 0 (length root))))
    ;; First use the 'nil' key to get generic variables.
    (project-install-bindings-from-alist (cdr (assq nil alist)))
    ;; Now apply the mode-specific variables.
    (project-install-bindings-from-alist (cdr (assq major-mode alist)))
    ;; Look for subdirectory matches in the class alist and apply
    ;; based on those.
    (mapc (lambda (elt)
	    (and (stringp (car elt))
		 (string= (car elt) (substring (buffer-file-name) 0
					       (length (car elt))))
		 (progn
		   ;; Again both generic and mode-specific.
		   (project-install-bindings-from-alist
		    (cdr (assq nil alist)))
		   (project-install-bindings-from-alist
		    (cdr (assq major-mode alist))))))
	  alist)
    ;; Special case C and derived modes.  Note that CC-based modes
    ;; don't work with derived-mode-p.  FIXME: this is arguably an
    ;; Emacs bug.  Perhaps we should be running
    ;; hack-local-variables-hook here instead?
    (and (boundp 'c-buffer-is-cc-mode)
	 c-buffer-is-cc-mode
	 (c-postprocess-file-styles))))

;;;###autoload
(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."
  (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)))
  (setq project-directory-alist
	(cons (cons directory class)
	      project-directory-alist)))

;;;###autoload
(defun define-project-bindings (class alist)
  "Map the project type CLASS to an alist of variable settings.
CLASS is the project class, a symbol.
ALIST is an alist that maps major modes to sub-alists.
Each sub-alist maps variable names to values.

Note that this does not filter risky variables.  This function
is intended for use by trusted code only."
  (let ((elt (assq class project-class-alist)))
    (if elt
	(setcdr elt alist)
      (setq project-class-alist
	    (cons (cons class alist)
		  project-class-alist)))))

;; There's a few ways we could do this.  We could use VC (with a VC
;; extension) and look for the root directory.  Or we could chain
;; settings files.  For now we choose a simple approach and let the
;; project maintainers be smart.
(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 "settings.el"))
	(setq result (concat dir "settings.el")))
       ((file-exists-p (concat dir ".settings.el"))
	(setq result (concat dir ".settings.el")))
       (t
	(setq dir (file-name-directory (directory-file-name dir))))))
    result))

;; Taken from Emacs 22.
(defun project-safe-local-variable-p (sym val)
  "Non-nil if SYM is safe as a file-local variable with value VAL.
It is safe if any of these conditions are met:

 * There is a matching entry (SYM . VAL) in the
   `safe-local-variable-values' user option.

 * The `safe-local-variable' property of SYM is a function that
   evaluates to a non-nil value with VAL as an argument."
  (or (member (cons sym val) safe-local-variable-values)
      (let ((safep (get sym 'safe-local-variable)))
        (and (functionp safep) (funcall safep val)))))

(unless (fboundp 'safe-local-variable-p)
  (fset 'safe-local-variable-p 'project-safe-local-variable-p))

(defun project-filter-risky-variables (alist)
  "Filter risky variables from the project settings ALIST.
This knows the expected structure of a project settings alist.
Actually this filters unsafe variables."
  (mapc (lambda (elt)
	  (let ((sub-alist (cdr elt)))
	    (if (stringp (car sub-alist))
		;; A string element maps to a secondary alist.
		(setcdr sub-alist
			(project-filter-risky-variables (cdr sub-alist)))
	      ;; Remove unsafe variables by setting their cars to nil.
	      ;; FIXME: or look only at risky-local-variable-p?
	      (mapc (lambda (sub-elt)
		      (unless (safe-local-variable-p (car sub-elt)
						     (cdr sub-elt))
			(setcar sub-elt nil)))
		    sub-alist)
	      ;; Now remove all the deleted risky variables.
	      (setcdr elt (assq-delete-all nil sub-alist)))))
	alist)
  alist)

(defun project-define-from-project-file (settings-file)
  "Load a settings file and register a new project class and instance.
The class name is the same as the directory in which the settings file
was found.  The settings have risky local variables filtered out."
  (with-temp-buffer
    (insert-file-contents settings-file)
    (let* ((dir-name (file-name-directory settings-file))
	   (class-name (intern dir-name))
	   (alist (project-filter-risky-variables (read (current-buffer)))))
      (define-project-bindings class-name alist)
      (set-directory-project dir-name class-name)
      class-name)))

;; Put this on find-file-hooks.
;;;###autoload
(defun project-find-file ()
  "Set local variables in a buffer based on project settings."
  (when (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 class (project-define-from-project-file settings))
	(setq root-dir (file-name-directory (buffer-file-name))))
       ((consp settings)
	(setq root-dir (car settings))
	(setq class (cdr settings))))
      (when class
	(make-local-variable 'project-class)
	(setq project-class class)
	(project-install-bindings class root-dir)))))

;;; project.el ends here

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-07-29 22:59     ` Tom Tromey
@ 2007-07-30 14:45       ` Stefan Monnier
  2007-07-30 15:08         ` Tom Tromey
  2007-07-31 17:05       ` Richard Stallman
  1 sibling, 1 reply; 28+ messages in thread
From: Stefan Monnier @ 2007-07-30 14:45 UTC (permalink / raw)
  To: tromey; +Cc: rms, joakim, emacs-devel

> First, you can create a "settings.el" or ".settings.el" file in a
> project's top-level directory.  This file holds a sexp that looks like

I feel uneasy about this name.  It seems just too generic.  How 'bout
"dir-local-settings" instead?

I'm also tempted to add "emacs" in the name, but I guess that the ".el"
extension can be considered sufficient information (and after all, we don't
use the "Emacs" name in the "Local Variables" section cookies either).


        Stefan

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-07-30 14:45       ` Stefan Monnier
@ 2007-07-30 15:08         ` Tom Tromey
  2007-08-03 17:29           ` Vagn Johansen
  0 siblings, 1 reply; 28+ messages in thread
From: Tom Tromey @ 2007-07-30 15:08 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: rms, joakim, emacs-devel

>>>>> "Stefan" == Stefan Monnier <monnier@iro.umontreal.ca> writes:

>> First, you can create a "settings.el" or ".settings.el" file in a
>> project's top-level directory.  This file holds a sexp that looks like

Stefan> I feel uneasy about this name.  It seems just too generic.

Yeah, I agree.

Stefan> How 'bout "dir-local-settings" instead?

Stefan> I'm also tempted to add "emacs" in the name, but I guess that
Stefan> the ".el" extension can be considered sufficient information
Stefan> (and after all, we don't use the "Emacs" name in the "Local
Stefan> Variables" section cookies either).

Mentioning Emacs wouldn't be too bad.  "dir-local-settings" is too
long for my taste.

How about ".emacs-settings"?

Tom

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-07-29 22:59     ` Tom Tromey
  2007-07-30 14:45       ` Stefan Monnier
@ 2007-07-31 17:05       ` Richard Stallman
  2007-07-31 18:45         ` Stefan Monnier
  2007-09-01 22:40         ` Tom Tromey
  1 sibling, 2 replies; 28+ messages in thread
From: Richard Stallman @ 2007-07-31 17:05 UTC (permalink / raw)
  To: tromey; +Cc: joakim, emacs-devel

    I did want to ask whether this is something that should be enabled by
    default.

Definitely.

It looks ready to install, as far as I can see, aside from some
minor points.  Do you disagree?


    (defun project-install-bindings-from-alist (mode-alist)
      "Apply local variable settings from MODE-ALIST."
      (mapc (lambda (pair)
	      (let ((variable (car pair))

Using dolist would be clearer (and more efficient).

    ;; - need a way to add to auto-mode-alist per-project
    ;;   e.g., semantic wants GCC's .def files to be c-mode
    ;;   (RMS points out that this is also easily done by adding comments
    ;;   to the particular files.)

The point is, we don't need another way to do it,
so that item should be deleted.

    ;; - maybe an easy way to integrate with customize?

That would be a very good feature to add.

    ;; - per-project and per-mode new file skeletons

I don't think that is very important.

Some doc comments:

The comments at the start of the file tend to use passive voice:

      Settings can be
    ;; checked in to the project's version control and they will
    ;; automatically be found and used by Emacs.

    ;; A project class can be defined manually using
    ;; `define-project-bindings'.  Then a directory can be associated with a
    ;; given class using `set-directory-project'.

The active voice is easier to read, and clearer, because it states the
subject.  Can you please rewrite all the passive voice sentences to active,
except when there is some very strong specific reason to use passive voice?

    ;; When a file is loaded, project.el will search up the directory
    ;; hierarchy.

Please stick to present tense except when that won't work.

;; (define-project-bindings 'gcc
;;   '((nil . ((indent-tabs-mode . t)
;; 	    (tab-width . 8)
;; 	    (fill-column . 80)))

That's not indented right.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-07-31 17:05       ` Richard Stallman
@ 2007-07-31 18:45         ` Stefan Monnier
  2007-09-01 22:40         ` Tom Tromey
  1 sibling, 0 replies; 28+ messages in thread
From: Stefan Monnier @ 2007-07-31 18:45 UTC (permalink / raw)
  To: rms; +Cc: tromey, joakim, emacs-devel

>     ;; - need a way to add to auto-mode-alist per-project
>     ;;   e.g., semantic wants GCC's .def files to be c-mode
>     ;;   (RMS points out that this is also easily done by adding comments
>     ;;   to the particular files.)

> The point is, we don't need another way to do it,
> so that item should be deleted.

BTW, another way to implement it is to match the directory portion in
auto-mode-alist.  E.g. you can have an entry such as
("/emacs/.*\\.[ch]\\'" . gnu-style-c-mode).

>     ;; When a file is loaded, project.el will search up the directory
>     ;; hierarchy.

Maybe you can try to use the new `locate-dominating-file' function.


        Stefan

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-07-30 15:08         ` Tom Tromey
@ 2007-08-03 17:29           ` Vagn Johansen
  2007-08-03 22:02             ` Richard Stallman
  0 siblings, 1 reply; 28+ messages in thread
From: Vagn Johansen @ 2007-08-03 17:29 UTC (permalink / raw)
  To: emacs-devel; +Cc: tromey

Tom Tromey <tromey@redhat.com> writes:

>>>>>> "Stefan" == Stefan Monnier <monnier@iro.umontreal.ca> writes:
>
>>> First, you can create a "settings.el" or ".settings.el" file in a
>>> project's top-level directory.  This file holds a sexp that looks like
>
> Stefan> I feel uneasy about this name.  It seems just too generic.
>
> Yeah, I agree.
>
> Stefan> How 'bout "dir-local-settings" instead?
>
> Stefan> I'm also tempted to add "emacs" in the name, but I guess that
> Stefan> the ".el" extension can be considered sufficient information
> Stefan> (and after all, we don't use the "Emacs" name in the "Local
> Stefan> Variables" section cookies either).
>
> Mentioning Emacs wouldn't be too bad.  "dir-local-settings" is too
> long for my taste.
>
> How about ".emacs-settings"?

Good choice. It also follows "tradition" because dirvars.el uses
.emacs-dirvars and dir-locals.el uses .emacs-locals.

May I suggest that project-find-file ignores remote files. I had an
Emacs instance going to 100% cpu usage when locating settings for
an FTP file.


Index: project.el
===================================================================
--- project.el	(revision 78)
+++ project.el	(working copy)
@@ -258,7 +258,7 @@
 ;;;###autoload
 (defun project-find-file ()
   "Set local variables in a buffer based on project settings."
-  (when (buffer-file-name)
+  (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) 



-- 
Vagn Johansen

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-08-03 17:29           ` Vagn Johansen
@ 2007-08-03 22:02             ` Richard Stallman
  2007-08-04  7:02               ` David Kastrup
  0 siblings, 1 reply; 28+ messages in thread
From: Richard Stallman @ 2007-08-03 22:02 UTC (permalink / raw)
  To: Vagn Johansen; +Cc: tromey, emacs-devel

    > How about ".emacs-settings"?

    Good choice. It also follows "tradition" because dirvars.el uses
    .emacs-dirvars and dir-locals.el uses .emacs-locals.

Variable names should say what the values mean.  To start them with
period is very undesirable.


, and this one should
follow that rule.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-08-03 22:02             ` Richard Stallman
@ 2007-08-04  7:02               ` David Kastrup
  2007-08-05  3:05                 ` Richard Stallman
  0 siblings, 1 reply; 28+ messages in thread
From: David Kastrup @ 2007-08-04  7:02 UTC (permalink / raw)
  To: rms; +Cc: Vagn Johansen, tromey, emacs-devel

Richard Stallman <rms@gnu.org> writes:

>     > How about ".emacs-settings"?
>
>     Good choice. It also follows "tradition" because dirvars.el uses
>     .emacs-dirvars and dir-locals.el uses .emacs-locals.
>
> Variable names should say what the values mean.  To start them with
> period is very undesirable.

I think we are talking about file names here.

-- 
David Kastrup, Kriemhildstr. 15, 44793 Bochum

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-08-04  7:02               ` David Kastrup
@ 2007-08-05  3:05                 ` Richard Stallman
  0 siblings, 0 replies; 28+ messages in thread
From: Richard Stallman @ 2007-08-05  3:05 UTC (permalink / raw)
  To: David Kastrup; +Cc: gonz808, tromey, emacs-devel

    > Variable names should say what the values mean.  To start them with
    > period is very undesirable.

    I think we are talking about file names here.

Sorry, I couldn't tell that.

.emacs-settings is fine as a file name.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-07-31 17:05       ` Richard Stallman
  2007-07-31 18:45         ` Stefan Monnier
@ 2007-09-01 22:40         ` Tom Tromey
  2007-09-02 11:56           ` Vagn Johansen
                             ` (2 more replies)
  1 sibling, 3 replies; 28+ messages in thread
From: Tom Tromey @ 2007-09-01 22:40 UTC (permalink / raw)
  To: rms; +Cc: joakim, emacs-devel

>>>>> "rms" == Richard Stallman <rms@gnu.org> writes:

[project.el]
rms> It looks ready to install, as far as I can see, aside from some
rms> minor points.  Do you disagree?

I do agree.  I've made the changes you suggested, and I've finally
updated the documentation and NEWS.  Patch appended.  Let me know what
you think; I can revise (perhaps slowly :-) if needed.

>     ;; - per-project and per-mode new file skeletons
rms> I don't think that is very important.

FWIW I left this comment in.  When we set up Eclipse for use with GNU
Classpath and for Mauve, having new-file skeletons was quite helpful
-- it meant that contributors could make a new file and have the right
thing happen, without them necessarily having to research what that
was.

Tom

man/ChangeLog:
2007-09-01  Tom Tromey  <tromey@redhat.com>

	* custom.texi (Project Variables): New node.
	(Variables): Updated.

lisp/ChangeLog:
2007-09-01  Tom Tromey  <tromey@redhat.com>

	* project.el: New file.
	* files.el (normal-mode): Call project-find-file.
	* loadup.el: Load project.

etc/ChangeLog:
2007-09-01  Tom Tromey  <tromey@redhat.com>

	* NEWS: Mention project.el.

Index: man/custom.texi
===================================================================
RCS file: /sources/emacs/emacs/man/custom.texi,v
retrieving revision 1.129
diff -u -r1.129 custom.texi
--- man/custom.texi	31 Mar 2007 09:06:48 -0000	1.129
+++ man/custom.texi	1 Sep 2007 22:55:22 -0000
@@ -795,6 +795,7 @@
 * Hooks::	        Hook variables let you specify programs for parts
 		          of Emacs to run on particular occasions.
 * Locals::	        Per-buffer values of variables.
+* Project Variables::   Per-project values of variables.
 * File Variables::      How files can specify variable values.
 @end menu
 
@@ -1051,6 +1052,57 @@
 (default-value 'fill-column)
 @end example
 
+@node Project Variables
+@subsection Per-Project Local Variables
+@cindex local variables in projects
+@cindex project local variables
+
+  Emacs provides a way to specify local variable values per-project.
+This can be done one of two ways.
+
+  The first approach is to put a special file in the root directory of
+a project.  When opening a local file, Emacs will search for this file
+and, if the file exists, Emacs will apply local variable settings from
+the file to the new buffer.  (This search is skipped for remote
+files.)
+
+  Emacs looks for files named @file{emacs-settings.el},
+@file{.emacs-settings.el}, or @file{.emacs-settings}, in that order.
+
+  When reading settings from a file like this, Emacs will
+automatically filter out risky local variables.  This makes it safe
+for projects to check in appropriate Emacs settings to their version
+control.
+
+  The file should hold a specially-constructed alist.  This alist maps
+Emacs mode names (symbols) to sub-alists; each sub-alist maps variable
+names to values.  The special mode name @samp{nil} means that the
+sub-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 . 8)
+         (fill-column . 80)))
+ (c-mode . ((c-file-style . "GNU")))
+ (java-mode . ((c-file-style . "GNU")))
+ ("libjava/classpath"
+  . ((nil . ((change-log-default-name . "ChangeLog.gcj")))))))
+@end example
+
+  This example, although incomplete, defines some settings for GNU
+GCC.  This will set @samp{indent-tabs-mode} to @samp{t} for any file
+in the source tree.  It will set the indentation style for any C or
+Java source file to @samp{GNU}.  Finally, it specifies a different
+@file{ChangeLog} file name for any file in the project that appears
+beneath the directory @file{libjava/classpath}.
+
+  The second approach to project-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}.
+
 @node File Variables
 @subsection Local Variables in Files
 @cindex local variables in files
Index: lisp/loadup.el
===================================================================
RCS file: /sources/emacs/emacs/lisp/loadup.el,v
retrieving revision 1.153
diff -u -r1.153 loadup.el
--- lisp/loadup.el	29 Aug 2007 05:28:07 -0000	1.153
+++ lisp/loadup.el	1 Sep 2007 22:55:25 -0000
@@ -68,6 +68,7 @@
 (load "bindings")
 (setq load-source-file-function 'load-with-code-conversion)
 (load "files")
+(load "project")
 
 (load "cus-face")
 (load "faces")  ; after here, `defface' may be used.
Index: lisp/files.el
===================================================================
RCS file: /sources/emacs/emacs/lisp/files.el,v
retrieving revision 1.927
diff -u -r1.927 files.el
--- lisp/files.el	31 Aug 2007 13:29:34 -0000	1.927
+++ lisp/files.el	1 Sep 2007 22:55:26 -0000
@@ -1910,6 +1910,7 @@
   (let ((enable-local-variables (or (not find-file) enable-local-variables)))
     (report-errors "File mode specification error: %s"
       (set-auto-mode))
+    (project-find-file)
     (report-errors "File local-variables error: %s"
       (hack-local-variables)))
   ;; Turn font lock off and on, to make sure it takes account of
Index: lisp/project.el
===================================================================
RCS file: lisp/project.el
diff -N lisp/project.el
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ lisp/project.el 1 Sep 2007 23:00:16 -0000
@@ -0,0 +1,257 @@
+;;; project.el --- per-project settings
+
+;; Copyright (C) 2007 Free Software Foundation, Inc.
+
+;; Author: Tom Tromey <tromey@redhat.com>
+;; Created: 27 Apr 2007
+;; Version: 0.3
+;; Keywords: tools
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
+
+;;; Commentary:
+
+;; This package makes it easy to set variables on a per-mode basis for
+;; an entire project.  This is much simpler than editing the local
+;; variables section of every file in the project.  You can check
+;; settings in to the project's version control and Emacs will
+;; automatically find and use them.
+
+;; You can manually define a project class using
+;; `define-project-bindings'.  Then you can associate a directory with
+;; a given class using `set-directory-project'.  This indirection lets
+;; you easily share settings between multiple instances of a project,
+;; for instance if you have multiple branches checked out.
+
+;; The alist argument to `define-project-bindings' is an alist with a
+;; special structure, defined below.
+
+;; When Emacs visits a file, project.el will search up the directory
+;; hierarchy.  If it finds a directory that was registered with
+;; `set-directory-project', it will use the corresponding project
+;; class.
+
+;; Otherwise, if a directory contains a file `emacs-settings.el',
+;; `.emacs-settings.el', or `.emacs-settings', then Emacs uses the
+;; contents of that file as an alist (of the type
+;; `define-project-bindings' takes).  In this case, Emacs first scrubs
+;; the alist of risky variable settings.
+
+;; Emacs uses the alist referred to above to set local variables.  The
+;; car of an entry in the alist is one of three things: the major mode
+;; (a symbol); `nil', which lists variables applied to every file in
+;; the project; or a string, which is a subdirectory of the project.
+
+;; If the car is a mode name or `nil', the cdr is an alist mapping
+;; variable names to variable values.
+
+;; If the car is a string, the cdr is another alist of the form above.
+
+;; Here's an example for GCC:
+
+;; (define-project-bindings 'gcc
+;;   '((nil . ((indent-tabs-mode . t)
+;;             (tab-width . 8)
+;;             (fill-column . 80)))
+;;     (c-mode . ((style . "GNU")))
+;;     (java-mode . ((style . "GNU")))
+;;     ("libjava/classpath"
+;;      . ((nil . ((change-log-default-name . "ChangeLog.gcj")))))))
+
+;; Note that in general you should only set project-related variables
+;; in a settings.el file that is checked in.  Things like key bindings
+;; (or electric keys for C mode) should probably not be set by the
+;; project.
+
+;;; ToDo:
+
+;; - should cache the mod time of settings file and then reload it
+;;   or at least offer user a way to invalidate cache
+;; - should let the user augment the project settings with personal ones
+;; - per-project and per-mode new file skeletons
+;; - maybe an easy way to integrate with customize?
+;; - make it easy to save per-project user settings like build
+;;   commands, gdb commands, etc
+;; - let user close a project and close all associated buffers; then
+;;   reopen the project and have the buffers open?
+;; - any role for bugzilla URLs, patch address and formatting?
+;; - got a request to emulate Eclipse's S-C-r binding; maybe integrate
+;;   with iswitch-fc or filename cache
+
+;;; Code:
+
+(defvar project-class-alist '()
+  "Alist mapping project class names (symbols) to project variable alists.")
+
+(defvar project-directory-alist '()
+  "Alist mapping project directory roots to project classes.")
+
+(defsubst project-get-alist (class)
+  "Return the project variable alist for project CLASS."
+  (cdr (assq class project-class-alist)))
+
+(defun project-install-bindings-from-alist (mode-alist)
+  "Apply local variable settings from MODE-ALIST."
+  (dolist (pair mode-alist)
+    (let ((variable (car pair))
+	  (value (cdr pair)))
+      (make-local-variable variable)
+      (set variable value))))
+
+(defun project-install-bindings (class root)
+  "Set variables for the current buffer for the given project class.
+CLASS is the project's class, a symbol.
+ROOT is the project's root directory, a string.
+This applies local variable settings in order from most generic
+to most specific."
+  (let* ((alist (project-get-alist class))
+	 (subdir (substring (buffer-file-name) 0 (length root))))
+    ;; First use the 'nil' key to get generic variables.
+    (project-install-bindings-from-alist (cdr (assq nil alist)))
+    ;; Now apply the mode-specific variables.
+    (project-install-bindings-from-alist (cdr (assq major-mode alist)))
+    ;; Look for subdirectory matches in the class alist and apply
+    ;; based on those.
+    (dolist (elt alist)
+      (and (stringp (car elt))
+	   (string= (car elt) (substring (buffer-file-name) 0
+					 (length (car elt))))
+	   (progn
+	     ;; Again both generic and mode-specific.
+	     (project-install-bindings-from-alist
+	      (cdr (assq nil alist)))
+	     (project-install-bindings-from-alist
+	      (cdr (assq major-mode alist))))))
+    ;; Special case C and derived modes.  Note that CC-based modes
+    ;; don't work with derived-mode-p.  FIXME: this is arguably an
+    ;; Emacs bug.  Perhaps we should be running
+    ;; hack-local-variables-hook here instead?
+    (and (boundp 'c-buffer-is-cc-mode)
+	 c-buffer-is-cc-mode
+	 (c-postprocess-file-styles))))
+
+;;;###autoload
+(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."
+  (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)))
+  (setq project-directory-alist
+	(cons (cons directory class)
+	      project-directory-alist)))
+
+;;;###autoload
+(defun define-project-bindings (class alist)
+  "Map the project type CLASS to an alist of variable settings.
+CLASS is the project class, a symbol.
+ALIST is an alist that maps major modes to sub-alists.
+Each sub-alist maps variable names to values.
+
+Note that this does not filter risky variables.  This function
+is intended for use by trusted code only."
+  (let ((elt (assq class project-class-alist)))
+    (if elt
+	(setcdr elt alist)
+      (setq project-class-alist
+	    (cons (cons class alist)
+		  project-class-alist)))))
+
+;; There's a few ways we could do this.  We could use VC (with a VC
+;; extension) and look for the root directory.  Or we could chain
+;; settings files.  For now we choose a simple approach and let the
+;; project maintainers be smart.
+(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 "emacs-settings.el"))
+	(setq result (concat dir "emacs-settings.el")))
+       ((file-exists-p (concat dir ".emacs-settings.el"))
+	(setq result (concat dir ".emacs-settings.el")))
+       ((file-exists-p (concat dir ".emacs-settings"))
+	(setq result (concat dir ".emacs-settings")))
+       (t
+	(setq dir (file-name-directory (directory-file-name dir))))))
+    result))
+
+(defun project-filter-risky-variables (alist)
+  "Filter risky variables from the project settings ALIST.
+This knows the expected structure of a project settings alist.
+Actually this filters unsafe variables."
+  (dolist (elt alist)
+    (let ((sub-alist (cdr elt)))
+      (if (stringp (car sub-alist))
+	  ;; A string element maps to a secondary alist.
+	  (setcdr sub-alist
+		  (project-filter-risky-variables (cdr sub-alist)))
+	;; Remove unsafe variables by setting their cars to nil.
+	;; FIXME: or look only at risky-local-variable-p?
+	(dolist (sub-elt sub-alist)
+	  (unless (safe-local-variable-p (car sub-elt) (cdr sub-elt))
+	    (setcar sub-elt nil)))
+	;; Now remove all the deleted risky variables.
+	(setcdr elt (assq-delete-all nil sub-alist)))))
+  alist)
+
+(defun project-define-from-project-file (settings-file)
+  "Load a settings file and register a new project class and instance.
+The class name is the same as the directory in which the settings file
+was found.  The settings have risky local variables filtered out."
+  (with-temp-buffer
+    (insert-file-contents settings-file)
+    (let* ((dir-name (file-name-directory settings-file))
+	   (class-name (intern dir-name))
+	   (alist (project-filter-risky-variables (read (current-buffer)))))
+      (define-project-bindings class-name alist)
+      (set-directory-project dir-name class-name)
+      class-name)))
+
+;;;###autoload
+(defun project-find-file ()
+  "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 class (project-define-from-project-file settings))
+	(setq root-dir (file-name-directory (buffer-file-name))))
+       ((consp settings)
+	(setq root-dir (car settings))
+	(setq class (cdr settings))))
+      (when class
+	(make-local-variable 'project-class)
+	(setq project-class class)
+	(project-install-bindings class root-dir)))))
+
+;;; project.el ends here
Index: etc/NEWS
===================================================================
RCS file: /sources/emacs/emacs/etc/NEWS,v
retrieving revision 1.1550
diff -u -r1.1550 NEWS
--- etc/NEWS	31 Aug 2007 08:11:26 -0000	1.1550
+++ etc/NEWS	1 Sep 2007 22:55:28 -0000
@@ -71,6 +71,9 @@
 ** The new command close-display-connection can be used to close a connection
 to a remote display, e.g. because the display is about to become unreachable.
 
+** project.el is now part of Emacs.  This provides for per-project
+settings of local variables.
+
 ** The command shell prompts for the default directory, when it is
 called with a prefix, and the default directory is a remote file name.
 This is because some file name handler (like ange-ftp) are not able to

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-09-01 22:40         ` Tom Tromey
@ 2007-09-02 11:56           ` Vagn Johansen
  2007-09-03  3:04             ` Richard Stallman
  2007-09-03 18:25           ` Richard Stallman
  2007-09-03 18:26           ` Richard Stallman
  2 siblings, 1 reply; 28+ messages in thread
From: Vagn Johansen @ 2007-09-02 11:56 UTC (permalink / raw)
  To: emacs-devel

Tom Tromey <tromey@redhat.com> writes:

>>>>>> "rms" == Richard Stallman <rms@gnu.org> writes:
>
> [project.el]
> rms> It looks ready to install, as far as I can see, aside from some
> rms> minor points.  Do you disagree?
>
> I do agree.  I've made the changes you suggested, and I've finally
> updated the documentation and NEWS.  Patch appended.  Let me know what
> you think; I can revise (perhaps slowly :-) if needed.


You still have a "FIXME" in a comment in
project-filter-risky-variables.

One problem is that not-safe variables are just ignored. When I first
tried project.el I could not understand why it did not work. I had to
debug to discover that lisp-indent-offset was not safe.

The obvious solution is to make it use the same UI as the existing
local variables functionality. See hack-local-variables-confirm in
files.el.


> +
> +(defun project-filter-risky-variables (alist)
> +  "Filter risky variables from the project settings ALIST.
> +This knows the expected structure of a project settings alist.
> +Actually this filters unsafe variables."
> +  (dolist (elt alist)
> +    (let ((sub-alist (cdr elt)))
> +      (if (stringp (car sub-alist))
> +	  ;; A string element maps to a secondary alist.
> +	  (setcdr sub-alist
> +		  (project-filter-risky-variables (cdr sub-alist)))
> +	;; Remove unsafe variables by setting their cars to nil.
> +	;; FIXME: or look only at risky-local-variable-p?
> +	(dolist (sub-elt sub-alist)
> +	  (unless (safe-local-variable-p (car sub-elt) (cdr sub-elt))
> +	    (setcar sub-elt nil)))
> +	;; Now remove all the deleted risky variables.
> +	(setcdr elt (assq-delete-all nil sub-alist)))))
> +  alist)
> +


-- 
Vagn Johansen

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-09-02 11:56           ` Vagn Johansen
@ 2007-09-03  3:04             ` Richard Stallman
  0 siblings, 0 replies; 28+ messages in thread
From: Richard Stallman @ 2007-09-03  3:04 UTC (permalink / raw)
  To: Vagn Johansen; +Cc: emacs-devel

    One problem is that not-safe variables are just ignored. When I first
    tried project.el I could not understand why it did not work. I had to
    debug to discover that lisp-indent-offset was not safe.

I made that a defcustom and marked it safe, but your point is still
valid in general.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-09-01 22:40         ` Tom Tromey
  2007-09-02 11:56           ` Vagn Johansen
@ 2007-09-03 18:25           ` Richard Stallman
  2007-09-04 21:53             ` Dan Nicolaescu
  2007-09-03 18:26           ` Richard Stallman
  2 siblings, 1 reply; 28+ messages in thread
From: Richard Stallman @ 2007-09-03 18:25 UTC (permalink / raw)
  To: tromey; +Cc: joakim, emacs-devel

    +  The first approach is to put a special file in the root directory of
    +a project.  When opening a local file, Emacs will search for this file
    +and, if the file exists, Emacs will apply local variable settings from
    +the file to the new buffer.  (This search is skipped for remote
    +files.)

1. Please stick to the present tense all the time in documentation
unless some other tense is mandatory.  So please say "Emacs searches".

Please look for other places in your text where this rule might apply.

2. It took me a couple of seconds to realize why the word "local" is
there and what it means, since in this context "local" usually means
"buffer-local".  I suggest using only the word "remote" and avoiding
use of "local" as its antonym.

3. Please avoid the passive voice, so please write "If the file is
remote, Emacs doesn't do this search, because it would be too slow."

    +  Emacs looks for files named @file{emacs-settings.el},
    +@file{.emacs-settings.el}, or @file{.emacs-settings}, in that order.

...and looks for them in the current directory, its parent, and so on
up the directory hierarchy.

    +  When reading settings from a file like this, Emacs will
    +automatically filter out risky local variables.  This makes it safe
    +for projects to check in appropriate Emacs settings to their version
    +control.

Whether they use version control is irrelevant; you can make the text
simpler by avoiding that tangent.

    +  This example, although incomplete, defines some settings for GNU
    +GCC.

1. In what sense is it imcomplete?

2. "GNU GCC" is redundant.

3. In what sense are these settings "for GCC"?  That is not clear.  I
first interpreted those words as meaning these settings affect use of
GCC.  Do you actually mean that they are used for the source code of
GCC?

4. Why include (tab-width . 8)?  That is the default?

    +  The second approach to project-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}.

That needs to give a little more information
or else refer to a place to find more.

    +** project.el is now part of Emacs.  This provides for per-project
    +settings of local variables.
    +

The file name is irrelevant -- please describe the feature, and the
user-interfaces (briefly is enough).

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-09-01 22:40         ` Tom Tromey
  2007-09-02 11:56           ` Vagn Johansen
  2007-09-03 18:25           ` Richard Stallman
@ 2007-09-03 18:26           ` Richard Stallman
  2 siblings, 0 replies; 28+ messages in thread
From: Richard Stallman @ 2007-09-03 18:26 UTC (permalink / raw)
  To: tromey; +Cc: joakim, emacs-devel

Since this is just 150 lines, I think it is better to add it to
files.el rather than keep it as a separate file.  Also, the user-level
documentation which is now in the manual need not be kept in comments.

The functions set-directory-project and define-project-bindings need
more info in their doc strings.  The doc strings should be sufficient
to tell a person how to use these functions.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-09-03 18:25           ` Richard Stallman
@ 2007-09-04 21:53             ` Dan Nicolaescu
  2007-09-05  3:16               ` Stefan Monnier
  2007-09-05  6:16               ` Richard Stallman
  0 siblings, 2 replies; 28+ messages in thread
From: Dan Nicolaescu @ 2007-09-04 21:53 UTC (permalink / raw)
  To: rms; +Cc: tromey, joakim, emacs-devel

Richard Stallman <rms@gnu.org> writes:

  >     +  The first approach is to put a special file in the root directory of
  >     +a project.  When opening a local file, Emacs will search for this file
  >     +and, if the file exists, Emacs will apply local variable settings from
  >     +the file to the new buffer.  (This search is skipped for remote
  >     +files.)
  > 
  > 1. Please stick to the present tense all the time in documentation
  > unless some other tense is mandatory.  So please say "Emacs searches".
  > 
  > Please look for other places in your text where this rule might apply.
  > 
  > 2. It took me a couple of seconds to realize why the word "local" is
  > there and what it means, since in this context "local" usually means
  > "buffer-local".  I suggest using only the word "remote" and avoiding
  > use of "local" as its antonym.
  > 
  > 3. Please avoid the passive voice, so please write "If the file is
  > remote, Emacs doesn't do this search, because it would be too slow."
  > 
  >     +  Emacs looks for files named @file{emacs-settings.el},
  >     +@file{.emacs-settings.el}, or @file{.emacs-settings}, in that order.
  > 
  > ...and looks for them in the current directory, its parent, and so on
  > up the directory hierarchy.

Can we please reduce the number of the files that are looked up from 3
to 2?  
Do we need both .emacs-settings.el and .emacs-settings? IMHO one of
them should be enough.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-09-04 21:53             ` Dan Nicolaescu
@ 2007-09-05  3:16               ` Stefan Monnier
  2007-09-05 17:32                 ` Dan Nicolaescu
  2007-09-05  6:16               ` Richard Stallman
  1 sibling, 1 reply; 28+ messages in thread
From: Stefan Monnier @ 2007-09-05  3:16 UTC (permalink / raw)
  To: Dan Nicolaescu; +Cc: tromey, rms, joakim, emacs-devel

> Can we please reduce the number of the files that are looked up from 3
> to 2?  
> Do we need both .emacs-settings.el and .emacs-settings? IMHO one of
> them should be enough.

Even just one seems to be plenty.
Either .emacs-settings or .emacs-settings.el (no preference between the
two).


        Stefan

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-09-04 21:53             ` Dan Nicolaescu
  2007-09-05  3:16               ` Stefan Monnier
@ 2007-09-05  6:16               ` Richard Stallman
  2007-09-05 14:06                 ` Stefan Monnier
  1 sibling, 1 reply; 28+ messages in thread
From: Richard Stallman @ 2007-09-05  6:16 UTC (permalink / raw)
  To: Dan Nicolaescu; +Cc: tromey, joakim, emacs-devel

    Can we please reduce the number of the files that are looked up from 3
    to 2?  
    Do we need both .emacs-settings.el and .emacs-settings? IMHO one of
    them should be enough.

If the time to search for these files is an issue, we could look for
just one file, .emacs-settings.el.  Is it really an issue?

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-09-05  6:16               ` Richard Stallman
@ 2007-09-05 14:06                 ` Stefan Monnier
  2007-09-05 14:36                   ` Davis Herring
  0 siblings, 1 reply; 28+ messages in thread
From: Stefan Monnier @ 2007-09-05 14:06 UTC (permalink / raw)
  To: rms; +Cc: tromey, Dan Nicolaescu, joakim, emacs-devel

>     Can we please reduce the number of the files that are looked up from
>     3 to 2?   Do we need both .emacs-settings.el and .emacs-settings? IMHO
>     one of them should be enough.

> If the time to search for these files is an issue, we could look for
> just one file, .emacs-settings.el.  Is it really an issue?

I don't think it's an issue but I don't see any reason to search for more
than one file for now.  We'll probably (based on past history for other
config files) want/need to add more file names in the future for various
reasons, but for now I don't see any.


        Stefan

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-09-05 14:06                 ` Stefan Monnier
@ 2007-09-05 14:36                   ` Davis Herring
  0 siblings, 0 replies; 28+ messages in thread
From: Davis Herring @ 2007-09-05 14:36 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: tromey, Dan Nicolaescu, rms, joakim, emacs-devel

>> If the time to search for these files is an issue, we could look for
>> just one file, .emacs-settings.el.  Is it really an issue?
>
> I don't think it's an issue but I don't see any reason to search for more
> than one file for now.  We'll probably (based on past history for other
> config files) want/need to add more file names in the future for various
> reasons, but for now I don't see any.

I think if we divide the number of filenames to check by 3, it might
become reasonable (at least as a user option) to check for project files
on remote systems.  For a new feature, it's surely worth making it more
usable rather than more compatible.

Davis

-- 
This product is sold by volume, not by mass.  If it appears too dense or
too sparse, it is because mass-energy conversion has occurred during
shipping.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-09-05  3:16               ` Stefan Monnier
@ 2007-09-05 17:32                 ` Dan Nicolaescu
  2007-09-05 18:18                   ` Stefan Monnier
  2007-09-05 18:28                   ` David Kastrup
  0 siblings, 2 replies; 28+ messages in thread
From: Dan Nicolaescu @ 2007-09-05 17:32 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: tromey, rms, joakim, emacs-devel

Stefan Monnier <monnier@iro.umontreal.ca> writes:

  > > Can we please reduce the number of the files that are looked up from 3
  > > to 2?  
  > > Do we need both .emacs-settings.el and .emacs-settings? IMHO one of
  > > them should be enough.
  > 
  > Even just one seems to be plenty.
  > Either .emacs-settings or .emacs-settings.el (no preference between the
  > two).

The reason I thought about 2 was so that we can have a .emacs-settings
file and another one that does not start with a dot for
filesystems/OSes that have issues with files that start with a dot.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-09-05 18:18                   ` Stefan Monnier
@ 2007-09-05 18:04                     ` Tom Tromey
  0 siblings, 0 replies; 28+ messages in thread
From: Tom Tromey @ 2007-09-05 18:04 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Dan Nicolaescu, rms, joakim, emacs-devel

>>>>> "Stefan" == Stefan Monnier <monnier@iro.umontreal.ca> writes:

Stefan> We have only .emacs and it works fine (yes I know we had _emacs at some
Stefan> point but this is not needed any more).  If there's any problem with
Stefan> .emacs-settings, convert-standard-filename will deal with it.

I was planning to use .emacs-settings.el, but I suppose
convert-standard-filename will handle that as well.

I'm working on this plus the other requested changes.

Tom

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-09-05 17:32                 ` Dan Nicolaescu
@ 2007-09-05 18:18                   ` Stefan Monnier
  2007-09-05 18:04                     ` Tom Tromey
  2007-09-05 18:28                   ` David Kastrup
  1 sibling, 1 reply; 28+ messages in thread
From: Stefan Monnier @ 2007-09-05 18:18 UTC (permalink / raw)
  To: Dan Nicolaescu; +Cc: tromey, rms, joakim, emacs-devel

>> > Can we please reduce the number of the files that are looked up from 3
>> > to 2?
>> > Do we need both .emacs-settings.el and .emacs-settings? IMHO one of
>> > them should be enough.
>> 
>> Even just one seems to be plenty.
>> Either .emacs-settings or .emacs-settings.el (no preference between the
>> two).

> The reason I thought about 2 was so that we can have a .emacs-settings
> file and another one that does not start with a dot for
> filesystems/OSes that have issues with files that start with a dot.

We have only .emacs and it works fine (yes I know we had _emacs at some
point but this is not needed any more).  If there's any problem with
.emacs-settings, convert-standard-filename will deal with it.


        Stefan

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: status of dir-vars or dir-locals inclusion in emacs?
  2007-09-05 17:32                 ` Dan Nicolaescu
  2007-09-05 18:18                   ` Stefan Monnier
@ 2007-09-05 18:28                   ` David Kastrup
  1 sibling, 0 replies; 28+ messages in thread
From: David Kastrup @ 2007-09-05 18:28 UTC (permalink / raw)
  To: Dan Nicolaescu; +Cc: tromey, emacs-devel, Stefan Monnier, joakim, rms

Dan Nicolaescu <dann@ics.uci.edu> writes:

> Stefan Monnier <monnier@iro.umontreal.ca> writes:
>
>   > > Can we please reduce the number of the files that are looked up from 3
>   > > to 2?  
>   > > Do we need both .emacs-settings.el and .emacs-settings? IMHO one of
>   > > them should be enough.
>   > 
>   > Even just one seems to be plenty.
>   > Either .emacs-settings or .emacs-settings.el (no preference between the
>   > two).
>
> The reason I thought about 2 was so that we can have a
> .emacs-settings file and another one that does not start with a dot
> for filesystems/OSes that have issues with files that start with a
> dot.

For those file systems there already is a substitution mechanism in
place: for example, as far as Emacs is concerned, "~/.emacs" is the
startup file even for those file systems.

-- 
David Kastrup, Kriemhildstr. 15, 44793 Bochum

^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2007-09-05 18:28 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-07-03 13:48 status of dir-vars or dir-locals inclusion in emacs? joakim
2007-07-04  0:25 ` Tom Tromey
2007-07-10 22:01   ` Richard Stallman
2007-07-29 22:59     ` Tom Tromey
2007-07-30 14:45       ` Stefan Monnier
2007-07-30 15:08         ` Tom Tromey
2007-08-03 17:29           ` Vagn Johansen
2007-08-03 22:02             ` Richard Stallman
2007-08-04  7:02               ` David Kastrup
2007-08-05  3:05                 ` Richard Stallman
2007-07-31 17:05       ` Richard Stallman
2007-07-31 18:45         ` Stefan Monnier
2007-09-01 22:40         ` Tom Tromey
2007-09-02 11:56           ` Vagn Johansen
2007-09-03  3:04             ` Richard Stallman
2007-09-03 18:25           ` Richard Stallman
2007-09-04 21:53             ` Dan Nicolaescu
2007-09-05  3:16               ` Stefan Monnier
2007-09-05 17:32                 ` Dan Nicolaescu
2007-09-05 18:18                   ` Stefan Monnier
2007-09-05 18:04                     ` Tom Tromey
2007-09-05 18:28                   ` David Kastrup
2007-09-05  6:16               ` Richard Stallman
2007-09-05 14:06                 ` Stefan Monnier
2007-09-05 14:36                   ` Davis Herring
2007-09-03 18:26           ` Richard Stallman
2007-07-04 17:04 ` Vagn Johansen
2007-07-05  1:29 ` Richard Stallman

Code repositories for project(s) associated with this public inbox

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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).