Есть ли способ для последовательного подключения файлов .dir-locals.el?

15

Предположим, у меня есть каталог с этими файлами.

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

Когда я иду, чтобы создать файл в /foo/bar/baz/, я хотел бы последовательно соединить их так, чтобы /foo/.dir-locals.elсначала применялось , а затем /foo/bar/.dir-locals.el, а затем/foo/bar/baz/.dir-locals.el

Эрик Джонсон
источник
Связанная тема: Как я могу иметь второй .dir-locals? ,
Дан
Нет варианта, который бы это делал (я внимательно посмотрел на код), но это должно быть (почти наверняка) возможно с некоторым дополнительным кодом. У меня тоже есть польза, так что я мог бы изучить это ...
Константин
С elisp все возможно. :)
Эрик Джонсон

Ответы:

7

Основываясь на ответе здесь , мы сделаем это, посоветовав hack-dir-local-variablesпоискать одну директорию и проверить, доступен ли этот .dir-locals.elфайл для чтения. Он будет продолжать работать до тех пор, пока не найдет каталог, который нельзя прочитать .dir-locals.el.

В зависимости от значения walk-dir-locals-upwardфайлы могут быть прочитаны из текущего каталога вверх или из последнего .dir-locals.elнайденного вниз. По умолчанию используется значение downward, поэтому подкаталоги могут сглаживать настройки своих родителей.

(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
источник
Кажется, это предполагает, что каждый каталог в дереве (до некоторого уровня выше текущего пути) имеет .dir-locals.el. Будет это работать , если у меня есть дерево каталогов a/b/cи существует a/.dir-locals.elи a/b/c/.dir-locals.el, но нет a/b/.dir-locals.el(предположит , что я посещаю , a/b/c/foo.elи я хочу от настройки , a/.dir-locals.elкоторые должны применяться)?
Константин
1
Да, это то, что я предполагаю. Пропавшие дир-местные жители a/b/разрывают цепь. Он должен где-то остановиться, и если вы хотите, чтобы он продолжался, вы можете добавить пустые файлы dir-locals.
erikstokes
3
Кстати, я бы приветствовал патч для Emacs для поддержки связывания dir-locals из коробки.
Стефан
6

Вот другой способ сделать это.

Я определяю функцию, которая создает список всех каталогов в текущей иерархии каталогов.

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

Пример по порядку:

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

Теперь я могу добавить совет, hack-dir-local-variablesчтобы «притвориться», что мы посещаем файл в самой верхней части дерева, применить локальные настройки каталога, затем перейти на один уровень вниз, применить настройки снова и так далее.

(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)
Константин
источник