Apakah ada cara untuk membuat daisy chain file .dir-locals.el?

15

Misalkan saya memiliki direktori dengan file-file ini.

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

Ketika saya pergi untuk membuat file /foo/bar/baz/, saya ingin membuat rantai daisy bersama-sama sehingga /foo/.dir-locals.elberlaku pertama, dan kemudian /foo/bar/.dir-locals.el, dan kemudian/foo/bar/baz/.dir-locals.el

Eric Johnson
sumber
Tidak ada opsi yang akan melakukan itu (saya melihat kode cukup dekat), tetapi harus (hampir pasti) mungkin dengan beberapa kode tambahan. Saya punya kegunaan untuk itu juga, jadi saya mungkin melihat ini ...
Constantine
Dengan elisp, semua hal menjadi mungkin. :)
Eric Johnson

Jawaban:

7

Berdasarkan jawaban di sini , kami melakukan ini dengan menyarankan hack-dir-local-variablesuntuk mencari satu direktori dan memuat memeriksa apakah .dir-locals.elfile itu dapat dibaca. Ini akan terus naik sampai menemukan direktori tanpa dapat dibaca .dir-locals.el.

Tergantung pada nilai walk-dir-locals-upwardfile dapat dibaca dari direktori saat ini ke atas atau dari yang terakhir .dir-locals.elditemukan ke bawah. Downward adalah default sehingga subdirektori dapat merusak pengaturan orang tua mereka.

(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
sumber
Ini tampaknya mengharapkan bahwa setiap direktori di pohon (hingga tingkat tertentu dari jalur saat ini) memiliki a .dir-locals.el. Apakah ini akan berfungsi jika saya memiliki pohon direktori a/b/cdan ada a/.dir-locals.eldan a/b/c/.dir-locals.el, tetapi tidak a/b/.dir-locals.el(anggap saya mengunjungi a/b/c/foo.eldan saya ingin pengaturan a/.dir-locals.elditerapkan)?
Constantine
1
Ya, itulah yang saya asumsikan. Dir-penduduk setempat yang hilang dalam a/b/rantai putus. Itu harus berhenti di suatu tempat dan jika Anda ingin terus berjalan Anda dapat menambahkan file dir-lokal kosong.
erikstokes
3
BTW, saya akan menyambut patch untuk Emacs untuk mendukung dir-penduduk lokal di luar kotak.
Stefan
6

Inilah cara berbeda untuk melakukan ini.

Saya mendefinisikan fungsi yang menghasilkan daftar semua direktori dalam hirarki direktori saat ini.

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

Contohnya adalah dalam urutan:

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

Sekarang saya dapat menambahkan saran untuk hack-dir-local-variablesmembuatnya "berpura-pura" bahwa kami mengunjungi file di bagian paling atas pohon, menerapkan pengaturan direktori-lokal, lalu turun satu tingkat, menerapkan pengaturan lagi, dan sebagainya.

(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
sumber