Czy istnieje sposób na łańcuchowe połączenie plików .dir-locals.el?

15

Załóżmy, że mam katalog z tymi plikami.

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

Kiedy idę, aby utworzyć plik /foo/bar/baz/, chciałbym połączyć je w łańcuch tak, aby /foo/.dir-locals.elobowiązywało najpierw, a potem /foo/bar/.dir-locals.eli potem/foo/bar/baz/.dir-locals.el

Eric Johnson
źródło
Powiązany wątek: Jak mogę mieć sekundę .dir-locals? .
Dan
Nie ma takiej opcji (dość dokładnie przyjrzałem się kodowi), ale powinno być (prawie na pewno) możliwe z dodatkowym kodem. Ja też mam z tego pożytek, więc mogę przyjrzeć się temu ...
Constantine,
Dzięki elisp wszystkie rzeczy są możliwe. :)
Eric Johnson,

Odpowiedzi:

7

W oparciu o odpowiedź tutaj , robimy to, doradzając, hack-dir-local-variablesaby przejrzeć jeden katalog w górę i załadować sprawdzić, czy ten .dir-locals.elplik jest czytelny. Będzie kontynuował, aż znajdzie katalog, którego nie można odczytać .dir-locals.el.

W zależności od wartości walk-dir-locals-upwardplików można odczytać z bieżącego katalogu w górę lub z ostatniego .dir-locals.elznalezionego w dół. W dół jest ustawienie domyślne, dzięki czemu podkatalogi mogą blokować ustawienia swoich rodziców.

(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
źródło
Wydaje się oczekiwać, że każdy katalog w drzewie (do pewnego poziomu w górę od bieżącej ścieżki) ma .dir-locals.el. Czy to zadziała, jeśli mam drzewo katalogów a/b/ci istnieją a/.dir-locals.eli a/b/c/.dir-locals.el, ale nie a/b/.dir-locals.el(zakładam, że odwiedzam a/b/c/foo.eli chcę zastosować ustawienia a/.dir-locals.el)?
Constantine,
1
Tak, właśnie to zakładam. Brakujący miejscowi reżyserzy a/b/przerywają łańcuch. Musi gdzieś się zatrzymać, a jeśli chcesz, żebyś kontynuował, możesz dodać puste pliki katalogów lokalnych.
erikstokes,
3
BTW, z radością powitałbym łatkę dla Emacsa, która wesprze tworzenie łańcuchów dla lokalnych reżyserów po wyjęciu z pudełka.
Stefan
6

Oto inny sposób na zrobienie tego.

Definiuję funkcję, która tworzy listę wszystkich katalogów w bieżącej hierarchii katalogów.

(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) "" ()))

Przykład jest w kolejności:

(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")

Teraz mogę dodać radę, hack-dir-local-variablesaby „udawać”, że odwiedzamy plik na samej górze drzewa, zastosuj ustawienia lokalne katalogu, następnie zejdź o jeden poziom niżej, zastosuj ustawienia ponownie i tak dalej.

(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)
Konstantyn
źródło