¿Puedo recargar una biblioteca y hacer que defvar vuelva a asignar valores?

10

Estoy desarrollando una biblioteca y me gustaría volver a cargarla después de editarla sin salir de Emacs (suponga que está encendida load-path):

(load-library "myname")

Cuando hago esto, Emacs no recoge los cambios en las defvarvariables de enlace.

No quiero llamar eval-defun( C-M-x) manualmente en cada formulario de nivel superior. ¿ M-x eval-bufferRespeto defvar/ defcustom?

gavenkoa
fuente
1
Quizás (unload-feature 'myname)primero?
npostavs
Solo lo probé y no, al contrario eval-defun, no detecta los cambios defvar.
JeanPierre
1
@KaushalModi: No creo que sea un duplicado. Esta pregunta es sobre actuar sobre todos los defvars en un archivo o búfer, si lo entiendo correctamente.
Dibujó el
1
Normalmente uno nunca necesitaría evaluar solo los defvars. Además, el uso de OP load-fileimplica que quiere evaluar todo el archivo mientras se asegura de que los defvars sean reevaluados.
Kaushal Modi
2
Mi propio enfoque es evaluar-defun mientras cambio los valores. Es poco frecuente como para ser utilizable por mí. YMMV.
YoungFrog

Respuestas:

3

(progn (unload-feature 'your-lib) (load-library "your-lib"))

Esto funcionará siempre que cargue los defvars por primera vez al cargar la biblioteca a través de emacs, y no usar eval-defun, eval-bufferetc.

Cuando lo use require, load-libraryetc. Emacs hará un seguimiento de las variables y funciones que forman parte de su biblioteca, y las eliminará por completo cuando las use unload-feature.

Al escribir paquetes, encuentro que usar el código anterior es una mejor solución que ejecutarlo eval-defunmientras escribes un código nuevo para no entrar en estados intermedios.

Jordon Biondo
fuente
(info "(elisp) Loading"), (info "(elisp) Unloading")y unload-featurerequieren forcearg si la biblioteca es dependencia de otra biblioteca. ¡Gran respuesta! Me pregunto qué versión de Emacs comenzará a proporcionar descarga ...
gavenkoa
3

defvarno reasigna el valor de una variable de la misma manera que, digamos setqo setf. Una vez que una variable tiene un valor, defvar no lo toca.

De defvarla cadena de documentos:

(defvar SYMBOL &optional INITVALUE DOCSTRING)

Defina SÍMBOLO como una variable y devuelva SÍMBOLO.

...

El argumento opcional INITVALUE se evalúa y se usa para establecer SYMBOL, solo si el valor de SYMBOL es nulo. Si SYMBOL es local en el búfer, su valor predeterminado es el establecido; los valores locales del búfer no se ven afectados. Si falta INITVALUE, el valor de SYMBOL no se establece.

...

Como presumiblemente defvareditó las variables en cuestión para darles valores cuando cargó la biblioteca por primera vez, volver a cargar la biblioteca no cambiará los valores.

Consulte también el nodo manual elisp sobre Definición de variables globales .

En lugar de confiar en defvar, siempre puede reasignar valores con setq. Como opción alternativa, torpe, puede uninternusar los símbolos para que los defvars no los encuentren al recargar:

(defvar test-1 "test this")
(defvar test-2 "test this one, too")

test-1                                  ; => "test this"
test-2                                  ; => "test this one, too"

(defvar test-1 "trying to redefine")
(defvar test-2 "trying to redefine, too")

test-1                                  ; => "test this"
test-2                                  ; => "test this one, too"

(mapc #'unintern '(test-1 test-2))

test-1                                  ; => error!
test-2                                  ; => error!

(defvar test-1 "trying to redefine")
(defvar test-2 "trying to redefine, too")

test-1                                  ; => "trying to redefine"
test-2                                  ; => "trying to redefine, too"
Dan
fuente
2
En este contexto, es decir, cuando se desarrolla un paquete elisp, defvares lo correcto para usar. setqpodría personalizar las personalizaciones establecidas por usuarios individuales. OP está pidiendo una forma de forzar la sobrescritura de defvarvariables durante el desarrollo del paquete . Cambiar a setqrequeriría volver a defvarcuando se lanza el paquete.
Tyler
@ Tyler, sí, estoy de acuerdo en que defvares apropiado para el desarrollo de paquetes. Solo estoy señalando que defvarno reasigna valores, mientras que lo setqhace.
Dan
2

Prueba esto:

(defun foo ()
  "(Re-)evaluate all `defvar's in the buffer (or its restriction)."
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (while (not (eobp))
      (when (re-search-forward "\\s-*(defvar \\([^ \t\n(\"]+\\)[ \t\n]+[^)]" nil 'MOVE)
        (let ((old-value (make-symbol "t"))
              new-value value)
          (let ((debug-on-error old-value))
            (setq value (eval-defun-2))
            (setq new-value debug-on-error))
          (unless (eq old-value new-value)
            (setq debug-on-error new-value))
          value)))))

Eso solo usa el mismo código que eval-defunusa en un defvar. Atraviesa el búfer (o su restricción al reducirse), deteniéndose en cada uno defvary usando el eval-defuncódigo en él.

Dibujó
fuente
1

Después de escuchar que no hay una solución conveniente para la reevaluación del búfer con la reasignación de defvar's, hice una función simple que se transmite en eval-defun:

(defun my/eval-buffer ()
  "Evaluate entire buffer with re-assigning values to `defvar' / `defcustom'.
Useful during package development."
  (interactive)
  (save-excursion
    (beginning-of-buffer)
    (while (not (eobp))
      (eval-defun nil)
      (end-of-defun))))

Estructura de código inspirada en la eval-defun-2implementación. Es similar a ¿Cómo fuerzo la reevaluación de un defvar? solución.

Originalmente, quiero que la función de alto nivel vuelva a evaluar la biblioteca que se reinstaló a través del script de compilación, por lo que:

(defun my/load-library (library)
  "Evaluate entire library with re-assigning values to `defvar' / `defcustom'.
Useful during package development."
  (interactive
   (list (completing-read "Load library: "
                          (apply-partially 'locate-file-completion-table
                                           load-path
                                           '("" ".el")))))
  (with-temp-buffer
    (insert-file-contents (locate-file library load-path '("" ".el")))
    (my/eval-buffer)))

La solución Drew funciona incluso en anidados, defvarpero es difícil entender completamente el código.

También pienso en uninterntodos los símbolos basados ​​en el prefijo / expresión regular de símbolos (como sugirió Dan ) pero soy flojo para escribir prefijos cada vez ... Vea ¿Cómo puedo desvincular todas las definiciones de símbolos con un prefijo determinado?

gavenkoa
fuente