sábado, 4 de janeiro de 2014

XDG Directories for emacs.

Some time ago, I wrote a XDG Directory Specification for GNU Emacs. This is used to assign directories for emacs files under the XDG specification for the home directory.

The named directories are set from the environment variables mentioned in the standard. If no environment variables are present, the following defaults are used:

Symbol Environment variable
(+ /emacs)
Default directory
user-emacs-data-directory XDG_DATA_HOME ~/.local/share/emacs/
user-emacs-config-directory XDG_CONFIG_HOME ~/.config/emacs/
user-emacs-cache-directory XDG_CACHE_HOME ~/.cache/emacs/
user-emacs-lisp-directory `user-emacs-data-directory`/lisp/
user-documents-directory output from xdg-user-dir,
if such executable exists
~/Documents/

Now, the code:

;;; xdg-paths.el --- XDG Base Directory Specification paths for emacs.
;;;
;;;
;;; Copyright ©2011 Francisco Miguel Colaço 
;;; All rights reserved.
;;;
;;; This library is free software; you can redistribute it and/or
;;; modify it under the terms of the GNU Lesser General Public
;;; License as published by the Free Software Foundation; either
;;; version 3 of the License, or (at your option) any later version.
;;;
;;; This library 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
;;; Lesser General Public License for more details.
;;;
;;; You should have received a copy of the GNU Lesser General Public
;;; License along with this library; if not, write to the
;;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;;; Boston, MA 02111-1307, USA.
;;;
;;;
;;; Commentary:
;;;
;;; This package sets the directory according to the XDG Base
;;; Directory Specification.  The directories are given by the
;;; environment variables, falling back to the defaults enumerated
;;; in the standard.
;;;
;;; The $XDG_RUNTIME_DIR was not contemplated here, since we found
;;; no practical use for it.
;;;
;;; The defaults (or generated values) are:
;;;
;;;          Symbol                         Default
;;;  user-emacs-data-directory     ~/.local/share/emacs/
;;;  user-emacs-config-directory   ~/.config/emacs/
;;;  user-emacs-cache-directory    ~/.cache/emacs/
;;;  user-emacs-lisp-directory     `user-emacs-data-directory`/lisp
;;;  user-documents-directory      ~/Documents
;;;
;;; Some convenience functions are defined to locate files in these
;;; directories and to add user lisp to load-path.
;;;
;;; Some advantages are:
;;;
;;; Installation:
;;;
;;;   1. put xdg-paths in your own path.
;;;   2. Start your .emacs with (load-library 'xdg-paths) or,
;;;      write (load-library 'xdg-paths) in site-start.el
;;;
;;; Use cases:
;;;
;;; In my .emacs, I simply load a configuration hub file within
;;; user-emacs-config-directory with:
;;;
;;;   (load (locate-user-config-file "conf-init"))
;;;
;;; Within conf-init.el file, all other files are loaded with:
;;;
;;;   (dolist (module (list
;;;                    "edit" "frame" "programming" "tex" "xml"
;;;                    (concat "emacs-version-" (int-to-string emacs-major-version))
;;;                    (concat "window-system-" (symbol-name window-system))
;;;                    (concat "system-type-" (subst-char-in-string ?/ ?- (symbol-name system-type)))
;;;                    (concat "host-" (system-name))))
;;;     (load (locate-user-config-file (concat "conf-" module)) t))
;;;
;;; Adding to path from the user library just becomes:
;;;
;;;     (add-user-lisp-to-path "yasnippet")
;;;
;;; The user documents directory can be made the initial buffer in case
;;; no command line arguments are passed in:
;;;
;;;   (if (eql (length command-line-args) 1)
;;;      ;; No files were passed in the command line.
;;;      (setq initial-buffer-choice user-documents-directory))
;;;
;;;
;;; Caveat Emptor:
;;;
;;; Variable setting and directory initialization were not properly tested.
;;; I have never tested setting previously the directories because the
;;; default results are satisfactory.
;;;
;;;
;;; History:
;;;
;;;  0.1.2: Tom Prince 
;;;    Require cl properly.
;;;    Fix some typos and dead code.
;;;  0.1.1: Francisco Miguel Colaço 
;;;    Removed stale code;
;;;    Replaced eq by zerop in xdg-user-dir.
;;;  0.1: Francisco Miguel Colaço 
;;;    Directory variables;
;;;    Functions to locate files;
;;;    add-to-path and add-user-lisp-to-path;
;;;    xdg-user-dir (depends on xdg-user-dir existing).
;;;
;;;
;;; Future work:
;;;
;;;   1. Make better docstrings (I may have assumed too much).
;;;   3. Add customize support.
;;;   3. Add more functions to cover the full standard.
;;;   4. Add more convenience functions (as needed by others).
;;;   5. Refactor the initialization of the variables.
;;;   6. Make it within the default emacs distribution.
;;;

;;; Code:

(eval-when-compile
  (require 'cl))

;;; Directories definition.
(defvar user-emacs-config-directory nil
  "The directory where the emacs user configuration files are stored at.")


(defvar user-emacs-data-directory nil
  "The directory where the emacs user data and lisp files are stored at.

\\[user-emacs-directory] is set to this directory.")


(defvar user-emacs-cache-directory nil
  "The directory where the emacs user expendable files are stored at.

Files stored here should not be missed when deleted, apart a
temporary loss in speed.")


(defvar user-emacs-lisp-directory nil
  "The directory where the user lisp packages are stored at.

This directory is added to \\[load-path].")


(defvar user-documents-directory nil
  "The directory where the user stores his documents.")



(defun xdg-user-dir (dirname)
  "Given NAME, run 'xdg-user-dir NAME' and return the result in a string.

If the command fails, return NIL."
  (let ((command (concat "xdg-user-dir " dirname)))
    (if (zerop (shell-command command))
 (substring (shell-command-to-string command) 0 -1)
      nil)))


(defun locate-user-file (filename &optional type)
  "Given a file, locate it in the user files.

If TYPE is NIL or 'data, the file will be located in user-emacs-data-directory.

If 'config, it will be located in user-emacs-config-directory.

If 'cache, it will be located in user-emacs-cache-directory.

If 'lisp, it will be located in user-emacs-lisp-directory.

If 'documents, it will be located in user-documents-directory.

If the category is wrong, an error will be signaled.
"
  (expand-file-name filename
      (case type
        ((nil data) user-emacs-data-directory)
        ('config user-emacs-config-directory)
        ('lisp user-emacs-lisp-directory)
        ('cache user-emacs-cache-directory)
        ('documents user-documents-directory)
        (t (error "The category %s is not valid" type)))))


(defun locate-user-config-file (filename)
  "Given a file, locate it in `user-emacs-config-directory`."
  (locate-user-file filename 'config))


(defun locate-user-lisp (filename)
  "Given a file, locate it in `user-emacs-lisp-directory`."
  (locate-user-file filename 'lisp))


(defun add-to-path (directory &optional append)
  "Given DIRECTORY, it it exists and is indeed a directory, add
it to `load-path`."
  (interactive "D")
  (if (file-directory-p directory)
      (add-to-list 'load-path directory append)
      (error "The directory \"%s\" does not exist or isn't a directory." directory)))


(defun add-user-lisp-to-path (directory &optional append)
  "Given DIRECTORY, it it exists and is indeed a directory, add
it to `load-path`."
  (interactive "D")
  (add-to-path (locate-user-lisp directory) append))


;; Set the default variables if they have no name.
(macrolet ((setq-if-null (variable value)
      `(if (null ,variable)
    (setf ,variable ,value)))
    (getdir (variable fallback)
      `(expand-file-name "emacs/" (or (getenv ,variable) ,fallback))))
  (setq-if-null user-emacs-config-directory (getdir "XDG_CONFIG_HOME" "~/.config/"))
  (setq-if-null user-emacs-data-directory (getdir "XDG_DATA_HOME" "~/.local/share/"))
  (setq-if-null user-emacs-cache-directory (getdir "XDG_CACHE_HOME" "~/.cache/"))
  (setq-if-null user-emacs-lisp-directory (expand-file-name "lisp" user-emacs-data-directory))
  (setq-if-null user-documents-directory (or (xdg-user-dir "DOCUMENTS") "~/Documents")))


;; Set the user-emacs-directory to user-emacs-data-directory.
(setf user-emacs-directory user-emacs-data-directory)


;; Add the user lisp directory to path.
(add-to-list 'load-path user-emacs-lisp-directory)


(provide 'xdg-paths)

Sem comentários:

Enviar um comentário