¿Cómo puedo acceder a las variables locales de directorio en mis enlaces de modo mayor?

He definido un archivo .dir-locals.el con el siguiente contenido:

((python-mode . ((cr/virtualenv-name . "saas")))) 

En mi .emacs tengo la siguiente función para recuperar este valor y proporcionar una ruta virtualenv:

 (defun cr/virtualenv () (cond (cr/virtualenv-name (format "%s/%s" virtualenv-base cr/virtualenv-name)) ((getenv "EMACS_VIRTUAL_ENV") (getenv "EMACS_VIRTUAL_ENV")) (t "~/.emacs.d/python"))) 

Finalmente, en mi lista de modo-enganche de python, tengo esta función de enganche:

 (add-hook 'python-mode-hook 'cr/python-mode-shell-setup) (defun cr/python-mode-shell-setup () (message "virtualenv-name is %s" cr/virtualenv-name) (let ((python-base (cr/virtualenv))) (cond ((and (fboundp 'ipython-shell-hook) (file-executable-p (concat python-base "/bin/ipython"))) (setq python-python-command (concat python-base "/bin/ipython")) (setq py-python-command (concat python-base "/bin/ipython")) (setq py-python-command-args '( "-colors" "NoColor"))) (t (setq python-python-command (concat python-base "/bin/python")) (setq py-python-command (concat python-base "/bin/python")) (setq py-python-command-args nil))))) 

Cuando abro un nuevo archivo python, el mensaje registrado por cr/python-mode-shell-setup indica que cr/virtualenv-name es nil . Sin embargo, cuando selecciono el nombre, obtengo “saas” en su lugar.

Obviamente hay un problema de orden de carga aquí; ¿hay alguna manera de hacer que mis sentencias de enganche de modo respondan a las variables de directorio local?

    Esto sucede porque normal-mode llamadas en normal-mode (set-auto-mode) y (hack-local-variables) en ese orden.

    Sin embargo, hack-local-variables-hook se ejecuta después de que se hayan procesado las variables locales, lo que permite algunas soluciones:

    1. La primera es hacer que Emacs ejecute un nuevo “enlace de variables locales” para cada modo principal:

       (add-hook 'hack-local-variables-hook 'run-local-vars-mode-hook) (defun run-local-vars-mode-hook () "Run a hook for the major-mode after the local variables have been processed." (run-hooks (intern (concat (symbol-name major-mode) "-local-vars-hook")))) (add-hook 'python-mode-local-vars-hook 'cr/python-mode-shell-setup) 

      (Su función original se puede utilizar sin modificar, con ese enfoque.)

    2. Una segunda opción es utilizar el argumento LOCAL opcional para add-hook que hace que la función especificada sea local en el búfer. Con este enfoque puedes escribir tu gancho de la siguiente manera:

       (add-hook 'python-mode-hook 'cr/python-mode-shell-setup) (defun cr/python-mode-shell-setup () (add-hook 'hack-local-variables-hook (lambda () (message "virtualenv-name is %s" cr/virtualenv-name) (let ((python-base (cr/virtualenv))) (cond ((and (fboundp 'ipython-shell-hook) (file-executable-p (concat python-base "/bin/ipython"))) (setq python-python-command (concat python-base "/bin/ipython")) (setq py-python-command (concat python-base "/bin/ipython")) (setq py-python-command-args '( "-colors" "NoColor"))) (t (setq python-python-command (concat python-base "/bin/python")) (setq py-python-command (concat python-base "/bin/python")) (setq py-python-command-args nil))))) nil t)) ; buffer-local hack-local-variables-hook 

      ie python-mode-hook ejecuta primero y registra la función anónima con hack-local-variables-hook para el búfer actual solamente; y esa función se llama después de que las variables locales han sido procesadas.

    3. El comentario de Lindydancer provoca un tercer enfoque. No es tan limpio como los otros dos, pero resultó interesante a pesar de todo. No me gustó la idea de hacer que (hack-local-variables) se llame dos veces, pero veo que si configura el local-enable-local-variables -buffer localmente, evita que (hack-local-variables) haciendo cualquier cosa, así que podrías hacer esto:

       (defun cr/python-mode-shell-setup () (report-errors "File local-variables error: %s" (hack-local-variables))) (set (make-local-variable 'local-enable-local-variables) nil) (let ((python-base (cr/virtualenv))) ...)) 

      Obviamente, eso modifica un poco la secuencia normal de ejecución, por lo que los efectos secundarios pueden ser posibles. Me preocupaba que si el mismo modo principal se establece mediante un comentario de variable local en el archivo, esto podría causar una recursión infinita, pero en realidad no parece ser un problema.

      Los comentarios del encabezado de la variable local (por ejemplo, -*- mode: foo -*- ) son manejados por (set-auto-mode) , por lo que están bien; pero un mode: foo Local Variables: comentar parece que sería un problema, ya que se maneja con (hack-local-variables) , y si el modo está configurado de esa manera, pensé que causaría recursión.

      En la práctica, pude desencadenar el problema usando una función simple como un ‘modo’ que no hizo nada más que intentar ejecutar sus ganchos; sin embargo, las pruebas con un modo ‘adecuado’ no mostraron el problema, por lo que probablemente sea seguro en la realidad. No miré esto más a fondo (ya que las otras dos soluciones son mucho más limpias que esto), pero supongo que el mecanismo de enganches de modo retardado probablemente lo explique.