Existe-t-il un moyen de chaîner des fichiers .dir-locals.el en guirlande?

15

Supposons que j'ai un répertoire avec ces fichiers.

/foo/bar/baz/.dir-locals.el
/foo/bar/.dir-locals.el
/foo/.dir-locals.el

Quand je vais créer un fichier dans /foo/bar/baz/, je voudrais les chaîner ensemble de telle sorte que cela /foo/.dir-locals.els'applique d'abord, puis /foo/bar/.dir-locals.el, puis/foo/bar/baz/.dir-locals.el

Eric Johnson
la source
Il n'y a aucune option qui ferait cela (j'ai regardé le code de près), mais cela devrait être (presque certainement) possible avec du code supplémentaire. J'en ai aussi une utilité, donc je pourrais y jeter un œil ...
Constantine
Avec elisp, tout est possible. :)
Eric Johnson

Réponses:

7

Sur la base de la réponse ici , nous le faisons en conseillant hack-dir-local-variablesde rechercher un répertoire et de vérifier si ce .dir-locals.elfichier est lisible. Il continuera jusqu'à ce qu'il trouve un répertoire sans lecture .dir-locals.el.

Selon la valeur des walk-dir-locals-upwardfichiers, il est possible de lire le répertoire courant vers le haut ou le dernier .dir-locals.eltrouvé vers le bas. Par défaut, la valeur par défaut permet aux sous-répertoires d'altérer les paramètres de leurs parents.

(defvar walk-dir-locals-upward nil
  "If non-nil, evaluate .dir-locals.el files starting in the
  current directory and going up. Otherwise they will be
  evaluated from the top down to the current directory.")

(defadvice hack-dir-local-variables (around walk-dir-locals-file activate)
  (let* ((dir-locals-list (list dir-locals-file))
         (walk-dir-locals-file (first dir-locals-list)))
    (while (file-readable-p (concat "../" walk-dir-locals-file))
      (progn
        (setq walk-dir-locals-file (concat "../" walk-dir-locals-file))
        (add-to-list 'dir-locals-list walk-dir-locals-file
                     walk-dir-locals-upward)
        ))
    (dolist (file dir-locals-list)
      (let ((dir-locals-file (expand-file-name file)))
        (message dir-locals-file)
        ad-do-it
        )))
  )
erikstokes
la source
Cela semble supposer que chaque répertoire de l'arborescence (jusqu'à un certain niveau par rapport au chemin actuel) possède un .dir-locals.el. Cela fonctionnera-t-il si j'ai une arborescence de répertoires a/b/cet qu'il existe a/.dir-locals.elet a/b/c/.dir-locals.el, mais non a/b/.dir-locals.el(supposons que je visite a/b/c/foo.elet que je souhaite que les paramètres a/.dir-locals.elsoient appliqués)?
Constantine
1
Oui, c'est ce que je suppose. Les dir-locals manquants a/b/cassent la chaîne. Il doit s'arrêter quelque part et si vous voulez qu'il continue, vous pouvez ajouter un fichier dir-local vide.
erikstokes
3
BTW, j'accueillerais un correctif pour Emacs pour prendre en charge le chaînage des dir-locals hors de la boîte.
Stefan
6

Voici une façon différente de procéder.

Je définis une fonction qui produit la liste de tous les répertoires de la hiérarchie de répertoires actuelle.

(defun file-name-directory-nesting-helper (name previous-name accumulator)
  (if (string= name previous-name)
      accumulator                       ; stop when names stop changing (at the top)
      (file-name-directory-nesting-helper
       (directory-file-name (file-name-directory name))
       name
       (cons name accumulator))))

(defun file-name-directory-nesting (name)
  (file-name-directory-nesting-helper (expand-file-name name) "" ()))

Un exemple est en règle:

(file-name-directory-nesting "/foo/bar/baz/quux/foo.el")
;; => ("/" "/foo" "/foo/bar" "/foo/bar/baz" "/foo/bar/baz/quux" "/foo/bar/baz/quux/foo.el")

Maintenant, je peux ajouter des conseils pour hack-dir-local-variablesfaire "semblant" que nous visitons un fichier tout en haut de l'arborescence, appliquer les paramètres locaux du répertoire, puis descendre d'un niveau, appliquer à nouveau les paramètres, etc.

(defun hack-dir-local-variables-chained-advice (orig)
  "Apply dir-local settings from the whole directory hierarchy,
from the top down."
  (let ((original-buffer-file-name (buffer-file-name))
        (nesting (file-name-directory-nesting (or (buffer-file-name)
                                                  default-directory))))
    (unwind-protect
        (dolist (name nesting)
          ;; make it look like we're in a directory higher up in the
          ;; hierarchy; note that the file we're "visiting" does not
          ;; have to exist
          (setq buffer-file-name (expand-file-name "ignored" name))
          (funcall orig))
      ;; cleanup
      (setq buffer-file-name original-buffer-file-name))))

(advice-add 'hack-dir-local-variables :around
            #'hack-dir-local-variables-chained-advice)
Constantine
la source