Existe uma maneira de encadear arquivos .dir-locals.el em cadeia?

15

Suponha que eu tenha um diretório com esses arquivos.

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

Quando vou criar um arquivo /foo/bar/baz/, gostaria de encadear uma série de maneira que /foo/.dir-locals.else aplique primeiro e depois /foo/bar/.dir-locals.ele depois/foo/bar/baz/.dir-locals.el

Eric Johnson
fonte
Tópico relacionado: Como posso ter um segundo .dir-locals? .
Dan
Não há opção que faça isso (observei o código com muita atenção), mas deveria ser (quase certamente é) possível com algum código extra. Eu tenho um uso para ele também, então eu poderia olhar para isso ...
Constantino
Com elisp, tudo é possível. :)
Eric Johnson

Respostas:

7

Com base na resposta aqui , fazemos isso aconselhando hack-dir-local-variablesa procurar um diretório e carregar se o .dir-locals.elarquivo está legível. Ele continuará subindo até encontrar um diretório sem legibilidade .dir-locals.el.

Dependendo do valor dos walk-dir-locals-upwardarquivos, pode ser lido do diretório atual para cima ou do último .dir-locals.elencontrado para baixo. Para baixo é o padrão, para que os subdiretórios possam ignorar as configurações de seus pais.

(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
fonte
Isso parece esperar que todo diretório na árvore (até algum nível acima do caminho atual) possua um .dir-locals.el. Funcionará se eu tiver uma árvore de diretórios a/b/ce existir a/.dir-locals.ele a/b/c/.dir-locals.el, mas não a/b/.dir-locals.el(suponha que eu esteja visitando a/b/c/foo.ele deseje que as configurações a/.dir-locals.elsejam aplicadas)?
Constantine
11
Sim, é o que estou assumindo. Os dir-locals ausentes a/b/quebram a cadeia. Ele precisa parar em algum lugar e, se você quiser continuar, pode adicionar um arquivo dir-locals vazio.
Erikstokes
3
BTW, eu gostaria de receber um patch para o Emacs para apoiar encadeamento dir-locals fora da caixa.
Stefan
6

Aqui está uma maneira diferente de fazer isso.

Defino uma função que produz a lista de todos os diretórios na hierarquia de diretórios atual.

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

Um exemplo está em ordem:

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

Agora, posso adicionar conselhos para hack-dir-local-variablesfazê-lo "fingir" que estamos visitando um arquivo na parte superior da árvore, aplicar configurações de diretório local e, em seguida, descer um nível, aplicar configurações novamente e assim por diante.

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