all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Paul Eggert <eggert@cs.ucla.edu>
To: Eli Zaretskii <eliz@gnu.org>, Stefan Monnier <monnier@iro.umontreal.ca>
Cc: Emacs Development <Emacs-devel@gnu.org>
Subject: [Emacs-diffs] master a4144af 1/2: Prefer ~/.config/emacs to ~/.emacs.d if neither exists
Date: Mon, 2 Sep 2019 12:03:45 -0700	[thread overview]
Message-ID: <1fc485fd-94ba-2988-1884-4ca024c03d4c@cs.ucla.edu> (raw)
In-Reply-To: <83lfv886i6.fsf@gnu.org>

[-- Attachment #1: Type: text/plain, Size: 687 bytes --]

Eli Zaretskii wrote:
> We cannot assume the user prefers the XDG conventions

We also cannot assume that a new user prefers the traditional conventions. On 
the GNU/Linux platform that I'm typing this message on, XDG conventions are 
commonly used by applications and are friendlier to new users.

That being said, you and Stefan both voiced objections, so I slaved away on a 
hot stove and wrote the attached proposed patch, which I hope addresses them.

This area has been quite a mess for some time, and it's not much fun to work on 
it - no wonder nobody else volunteered! If it's any consolation, the attached 
patch does shrink the number of lines in the source, ever so slightly.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Assume-XDG-only-in-XDGish-environments.patch --]
[-- Type: text/x-patch; name="0001-Assume-XDG-only-in-XDGish-environments.patch", Size: 16897 bytes --]

From b865f291ea06536aec28744b7bf896ed36aceace Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Mon, 2 Sep 2019 11:49:08 -0700
Subject: [PATCH] Assume XDG only in XDGish environments
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Do not assume XDG conventions for configuration file names
when starting in a home directory that has no Emacs
configuration files.  Instead, use XDG conventions only in
XDGish environments.
* doc/emacs/custom.texi (Find Init):
* doc/lispref/os.texi (Init File):
* etc/NEWS: Describe adjusted behavior.
* lisp/files.el (locate-user-emacs-file):
Do not use (make-directory user-emacs-directory t), as that’s
too aggressive: we don’t want to make ancestors of parents.
Instead, just make the parent directory if needed for XDG.
* lisp/startup.el (startup--xdg-config-default)
(startup--xdg-config-home-emacs, startup--xdg-or-homedot):
Remove, replacing with ...
(startup--xdg-config-home-default, startup--xdg-config-home)
(startup--init-dir-file-cache, startup--init-dir-file):
... these new vars and function.  All uses changed.
(normal-top-level):
Implement the changed behavior.
(startup--load-user-init-file): New args DIR and FILE,
replacing old args FILENAME-FUNCTION and
ALTERNATE-FILENAME-FUNCTION.  This simplifies both callers and
callees, now that the callers already check for home-directory
init files.  All callers changed.
---
 doc/emacs/custom.texi |  16 ++--
 doc/lispref/os.texi   |  11 +--
 etc/NEWS              |   2 +-
 lisp/files.el         |  10 ++-
 lisp/startup.el       | 181 ++++++++++++++++++++----------------------
 5 files changed, 109 insertions(+), 111 deletions(-)

diff --git a/doc/emacs/custom.texi b/doc/emacs/custom.texi
index 0c2509e1cd..eef0ff9a4f 100644
--- a/doc/emacs/custom.texi
+++ b/doc/emacs/custom.texi
@@ -2638,15 +2638,13 @@ Find Init
 
   Emacs normally finds your init file in a location under your home
 directory.  @xref{Init File}.  By default this location is
-@file{~/.config/emacs/init.el} where @file{~/} stands for your home directory.
-This default can be overridden as described below.
+@file{$XDG_CONFIG_HOME/emacs/init.el} if @env{XDG_CONFIG_HOME} is set
+in your environment, otherwise @file{~/.config/emacs/init.el} if the
+directory @file{~/.config/emacs} exists, where @file{~/} stands for
+your home directory.
 
-  If @env{XDG_CONFIG_HOME} is set in your environment, its
-value replaces @file{~/.config} in the name of the default
-init file.
-
-  If the default init file's parent directory does not exist but the
-directory @file{~/.emacs.d} does exist, Emacs looks for your init file
+  If @env{XDG_CONFIG_HOME} is unset and @file{~/.config/emacs}
+is absent, Emacs looks for your init file
 using the filenames @file{~/.emacs.el}, @file{~/.emacs}, or
 @file{~/.emacs.d/init.el}; you can choose to use any one of these
 names.  (Note that only the locations directly in your home directory
@@ -2656,7 +2654,7 @@ Find Init
 that troubleshooting a problem that might be due to a bad init file,
 or archiving a collection of init files, can be done by renaming that
 directory.  To help older Emacs versions find configuration files in
-their current default locations, you can execute the following
+the @file{~/.config} location, you can execute the following
 Emacs Lisp code:
 
 @example
diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi
index c94e96bde8..69a8209882 100644
--- a/doc/lispref/os.texi
+++ b/doc/lispref/os.texi
@@ -474,12 +474,13 @@ Init File
 
 @defvar user-emacs-directory
 This variable holds the name of the Emacs default directory.
-It defaults to @file{$@{XDG_CONFIG_HOME-'~/.config'@}/emacs/}
-if that directory exists and @file{~/.emacs.d/} does not exist,
+It defaults to @file{$XDG_CONFIG_HOME/emacs/} if the environment
+variable @env{XDG_CONFIG_HOME} is set, otherwise to
+@file{~/.config/emacs/} if that directory exists,
 otherwise to @file{~/.emacs.d/} on all platforms but MS-DOS@.
-Here, @file{$@{XDG_CONFIG_HOME-'~/.config'@}}
-stands for the value of the environment variable @env{XDG_CONFIG_HOME}
-if that variable is set, and for @file{~/.config} otherwise.
+In an interactive session, the directory is created if necessary;
+in this case the @file{~/.emacs.d/} convention is assumed if a home-directory
+init file exists or if the platform does not follow the XDG convention.
 @xref{Find Init,,How Emacs Finds Your Init File, emacs, The GNU Emacs Manual}.
 @end defvar
 
diff --git a/etc/NEWS b/etc/NEWS
index d5130e9f3c..5eb3d15c30 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -145,7 +145,7 @@ and will override their traditional locations (the home directory,
 ~/.emacs.d, etc.).
 
 Emacs will still look for init files in their traditional locations if
-XDG_CONFIG_HOME does not exist, so invoking Emacs with
+XDG_CONFIG_HOME is unset and ~/.config/emacs is absent, so setting
 XDG_CONFIG_HOME='/nowhere' might be useful if your new-location init
 files are scrambled, or if you want to force Emacs to ignore files
 under XDG_CONFIG_HOME for some other reason.
diff --git a/lisp/files.el b/lisp/files.el
index ce4dd99bd5..7fc4f38ea9 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -1043,7 +1043,15 @@ locate-user-emacs-file
 		     (setq errtype "access"))
 	       (with-file-modes ?\700
 		 (condition-case nil
-		     (make-directory user-emacs-directory t)
+		     (condition-case nil
+			 (make-directory user-emacs-directory)
+		       (file-missing
+			(when (string-suffix-p "/.config/emacs/"
+					       user-emacs-directory)
+			  ;; Make both .config and .config/emacs, XDG style.
+			  (make-directory
+			   (substring user-emacs-directory 0 -7))
+			  (make-directory user-emacs-directory))))
 		   (error (setq errtype "create")))))
 	     (when (and errtype
 			user-emacs-directory-warning
diff --git a/lisp/startup.el b/lisp/startup.el
index a16db242da..5a530c17a0 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -490,26 +490,57 @@ normal-top-level-add-to-load-path
     (when tail
       (setcdr tail (append (mapcar 'expand-file-name dirs) (cdr tail))))))
 
-;; The default location for XDG-convention Emacs init files.
-(defconst startup--xdg-config-default "~/.config/emacs/")
-;; The location for XDG-convention Emacs init files.
-(defvar startup--xdg-config-home-emacs)
-
-;; Return the name of the init file directory for Emacs, assuming
-;; XDG-DIR is the XDG location and USER-NAME is the user name.
-;; If USER-NAME is nil or "", use the current user.
-;; Prefer the XDG location unless it does does not exist and the
-;; .emacs.d location does exist.
-(defun startup--xdg-or-homedot (xdg-dir user-name)
-  (if (file-exists-p xdg-dir)
-      xdg-dir
-    (let ((emacs-d-dir (concat "~" user-name
-			       (if (eq system-type 'ms-dos)
-				   "/_emacs.d/"
-				 "/.emacs.d/"))))
-      (if (file-exists-p emacs-d-dir)
-	  emacs-d-dir
-	xdg-dir))))
+;; The default location for XDG-convention configuration.
+(defconst startup--xdg-config-home-default "~/.config")
+;; The location for XDG-convention configuration.
+(defvar startup--xdg-config-home)
+;; A cons (INIT-DIR . INIT-FILE) computed during startup.
+(defvar startup--init-dir-file-cache)
+
+;; Return an (INIT-DIR . INIT-FILE) pair.
+;; The latter is either absolute, or relative to the former.
+;; XDG-CONFIG-HOME is the XDG config home and HOME-DIR the home directory.
+;; Prefer the XDG location if XDG-CONFIG-HOME/emacs exists, or
+;; if XDG-CONFIG-HOME exists and non-XDG init files are absent.
+(defun startup--init-dir-file (xdg-config-home home-dir)
+  (let ((xdg-emacs (concat xdg-config-home "/emacs/")))
+    (or (unless (file-accessible-directory-p xdg-emacs)
+	  (let* ((dot-emacs (concat home-dir
+				    (if (eq system-type 'ms-dos)
+					"/_emacs.d/" "/.emacs.d/")))
+		 (match (if (memq system-type '(windows-nt ms-dos))
+			    "\\`[._]emacs\\(\\.elc?\\)?\\'"
+			  "\\`\\.emacs\\(\\.elc?\\)?\\'"))
+		 (inits (ignore-errors
+			  (directory-files home-dir nil match t))))
+	    (when (or inits (file-accessible-directory-p dot-emacs)
+		      ;; No Emacs configuration files are present, so
+		      ;; choose their convention by looking at context.
+		      ;; Assume the XDG convention if it is already in use by
+		      ;; this user for other programs (a cheaper test),
+		      ;; or if the platform follows the XDG convention
+		      ;; (a more expensive test).
+		      (not (or (file-accessible-directory-p xdg-config-home)
+			       (executable-find "xdg-open"))))
+	      (cons dot-emacs
+		    (if (not inits)
+			"init"
+		      (concat
+		       home-dir
+		       (or (when (and (eq system-type 'windows-nt)
+				      (not (catch t
+					     (dolist (init inits)
+					       (when (eq ?. (aref init 0))
+						 (throw t t))))))
+			     (push `(initialization
+				     ,(format-message
+				       (concat
+					"`_emacs' init file is deprecated,"
+					" please use `.emacs'")))
+				   delayed-warnings-list)
+			     "/_emacs")
+			   "/.emacs")))))))
+	(cons xdg-emacs "init"))))
 
 (defun normal-top-level ()
   "Emacs calls this function when it first starts up.
@@ -520,13 +551,14 @@ normal-top-level
       (message internal--top-level-message)
     (setq command-line-processed t)
 
-    (setq startup--xdg-config-home-emacs
+    (setq startup--init-dir-file-cache
 	  (let ((xdg-config-home (getenv-internal "XDG_CONFIG_HOME")))
+	    (setq startup--xdg-config-home
+		  (or xdg-config-home startup--xdg-config-home-default))
 	    (if xdg-config-home
-		(concat xdg-config-home "/emacs/")
-	      startup--xdg-config-default)))
-    (setq user-emacs-directory
-	  (startup--xdg-or-homedot startup--xdg-config-home-emacs nil))
+		(cons (concat xdg-config-home "/emacs/") "init")
+	      (startup--init-dir-file startup--xdg-config-home-default "~"))))
+    (setq user-emacs-directory (car startup--init-dir-file-cache))
 
     ;; Look in each dir in load-path for a subdirs.el file.  If we
     ;; find one, load it, which will add the appropriate subdirs of
@@ -908,15 +940,10 @@ startup--setup-quote-display
           (when standard-display-table
             (aset standard-display-table char nil)))))))
 
-(defun startup--load-user-init-file
-    (filename-function &optional alternate-filename-function load-defaults)
-  "Load a user init-file.
-FILENAME-FUNCTION is called with no arguments and should return
-the name of the init-file to load.  If this file cannot be
-loaded, and ALTERNATE-FILENAME-FUNCTION is non-nil, then it is
-called with no arguments and should return the name of an
-alternate init-file to load.  If LOAD-DEFAULTS is non-nil, then
-load default.el after the init-file.
+(defun startup--load-user-init-file (dir file &optional load-defaults)
+  "Load a user init-file in directory DIR and named by FILE.
+If LOAD-DEFAULTS is non-nil, then load default.el after the
+init-file.
 
 This function sets `user-init-file' to the name of the loaded
 init-file, or to a default value if loading is not possible."
@@ -929,26 +956,16 @@ startup--load-user-init-file
     (let ((debug-on-error debug-on-error-initial))
       (condition-case-unless-debug error
           (when init-file-user
-            (let ((init-file-name (funcall filename-function)))
-
+            (let ((init-file-name (expand-file-name file dir)))
               ;; If `user-init-file' is t, then `load' will store
               ;; the name of the file that it loads into
               ;; `user-init-file'.
               (setq user-init-file t)
-	      (when init-file-name
-		(load (if (equal (file-name-extension init-file-name)
-				 "el")
-			  (file-name-sans-extension init-file-name)
-			init-file-name)
-		      'noerror 'nomessage))
-
-              (when (and (eq user-init-file t) alternate-filename-function)
-                (let ((alt-file (funcall alternate-filename-function)))
-                  (and (equal (file-name-extension alt-file) "el")
-                       (setq alt-file (file-name-sans-extension alt-file)))
-		  (unless init-file-name
-		    (setq init-file-name alt-file))
-                  (load alt-file 'noerror 'nomessage)))
+	      (load (if (equal (file-name-extension init-file-name)
+			       "el")
+			(file-name-sans-extension init-file-name)
+		      init-file-name)
+		    'noerror 'nomessage)
 
               ;; If we did not find the user's init file, set
               ;; user-init-file conclusively.  Don't let it be
@@ -1006,7 +1023,6 @@ startup--load-user-init-file
 (defun command-line ()
   "A subroutine of `normal-top-level'.
 Amongst another things, it parses the command-line arguments."
- (let (xdg-dir startup-init-directory)
   (setq before-init-time (current-time)
 	after-init-time nil
         command-line-default-directory default-directory)
@@ -1196,28 +1212,25 @@ command-line
                          :error))))
 
   ;; Calculate the name of the Emacs init directory.
-  ;; This is typically ~INIT-FILE-USER/.config/emacs unless the user
-  ;; is following the ~INIT-FILE-USER/.emacs.d convention.
-  (setq xdg-dir startup--xdg-config-home-emacs)
-  (setq startup-init-directory
-	(if (or (zerop (length init-file-user))
-		(and (eq xdg-dir user-emacs-directory)
-		     (not (eq xdg-dir startup--xdg-config-default))))
-	    user-emacs-directory
-	  ;; The name is not obvious, so access more directories to calculate it.
-	  (setq xdg-dir (concat "~" init-file-user "/.config/emacs/"))
-	  (startup--xdg-or-homedot xdg-dir init-file-user)))
+  ;; This is typically ~INIT-FILE-USER/.config/emacs/ if
+  ;; ~INIT-FILE-USER/.config exists, ~INIT-FILE-USER/.emacs.d/ otherwise.
+  (when (and (not (zerop (length init-file-user)))
+	     (eq startup--xdg-config-home
+		 startup--xdg-config-home-default))
+    ;; It might differ for init-file-user; access more dirs to calculate it.
+    (setq startup--init-dir-file-cache
+	  (let ((home-dir (concat "~" init-file-user)))
+	    (startup--init-dir-file (concat home-dir "/.config")
+				    home-dir))))
 
   ;; Load the early init file, if found.
   (startup--load-user-init-file
-   (lambda ()
-     (expand-file-name
-      ;; We use an explicit .el extension here to force
-      ;; startup--load-user-init-file to set user-init-file to "early-init.el",
-      ;; with the .el extension, if the file doesn't exist, not just
-      ;; "early-init" without an extension, as it does for ".emacs".
-      "early-init.el"
-      startup-init-directory)))
+    (car startup--init-dir-file-cache)
+    ;; We use an explicit .el extension here to force
+    ;; startup--load-user-init-file to set user-init-file to "early-init.el",
+    ;; with the .el extension, if the file doesn't exist, not just
+    ;; "early-init" without an extension, as it does for ".emacs".
+    "early-init.el")
   (setq early-init-file user-init-file)
 
   ;; If any package directory exists, initialize the package system.
@@ -1353,30 +1366,8 @@ command-line
 
     ;; Load that user's init file, or the default one, or none.
     (startup--load-user-init-file
-     (lambda ()
-       (cond
-	((eq startup-init-directory xdg-dir) nil)
-        ((eq system-type 'ms-dos)
-         (concat "~" init-file-user "/_emacs"))
-        ((not (eq system-type 'windows-nt))
-         (concat "~" init-file-user "/.emacs"))
-        ;; Else deal with the Windows situation.
-        ((directory-files "~" nil "^\\.emacs\\(\\.elc?\\)?$")
-         ;; Prefer .emacs on Windows.
-         "~/.emacs")
-        ((directory-files "~" nil "^_emacs\\(\\.elc?\\)?$")
-         ;; Also support _emacs for compatibility, but warn about it.
-         (push `(initialization
-                 ,(format-message
-                   "`_emacs' init file is deprecated, please use `.emacs'"))
-               delayed-warnings-list)
-         "~/_emacs")
-        (t ;; But default to .emacs if _emacs does not exist.
-         "~/.emacs")))
-     (lambda ()
-       (expand-file-name
-        "init"
-        startup-init-directory))
+     (car startup--init-dir-file-cache)
+     (cdr startup--init-dir-file-cache)
      (not inhibit-default-init))
 
     (when (and deactivate-mark transient-mark-mode)
@@ -1492,7 +1483,7 @@ command-line
   (if (and (boundp 'x-session-previous-id)
            (stringp x-session-previous-id))
       (with-no-warnings
-	(emacs-session-restore x-session-previous-id)))))
+	(emacs-session-restore x-session-previous-id))))
 
 (defun x-apply-session-resources ()
   "Apply X resources which specify initial values for Emacs variables.
-- 
2.21.0


  reply	other threads:[~2019-09-02 19:03 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-08-30 12:34 [Emacs-diffs] master a4144af 1/2: Prefer ~/.config/emacs to ~/.emacs.d if neither exists Eli Zaretskii
2019-08-30 14:20 ` Yuri D'Elia
2019-08-30 14:40   ` Eli Zaretskii
2019-09-01  6:25 ` Paul Eggert
2019-09-01 13:05   ` Stefan Monnier
2019-09-01 13:44     ` Teemu Likonen
2019-09-01 13:56     ` Eli Zaretskii
2019-09-02 19:03       ` Paul Eggert [this message]
     [not found] <20190830072451.9819.83340@vcs0.savannah.gnu.org>
     [not found] ` <20190830072452.ACB48210EC@vcs0.savannah.gnu.org>
2019-08-30 14:38   ` Stefan Monnier
2019-08-30 15:20     ` Kaushal Modi

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=1fc485fd-94ba-2988-1884-4ca024c03d4c@cs.ucla.edu \
    --to=eggert@cs.ucla.edu \
    --cc=Emacs-devel@gnu.org \
    --cc=eliz@gnu.org \
    --cc=monnier@iro.umontreal.ca \
    /path/to/YOUR_REPLY

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

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

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

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