Puis-je recharger une bibliothèque et demander à defvar de réaffecter des valeurs?

10

Je suis en train de développer une bibliothèque et je voudrais la recharger après avoir édité sans quitter Emacs (supposons qu'elle soit allumée load-path):

(load-library "myname")

Lorsque je fais cela, Emacs ne defvardétecte pas les modifications apportées aux variables liées.

Je ne veux pas appeler eval-defun( C-M-x) manuellement sur chaque formulaire de niveau supérieur. Est -ce que le M-x eval-bufferrespect defvar/ defcustom?

gavenkoa
la source
1
Peut-être d' (unload-feature 'myname)abord?
npostavs
Je viens de l'essayer et non, contrairement à eval-defuncela, il ne détecte pas de changements defvar.
JeanPierre
1
@KaushalModi: Je ne pense pas que ce soit un doublon. Cette question concerne l'action sur tous les defvars dans un fichier ou un tampon, si je comprends bien.
Drew
1
Normalement, on n'aurait jamais besoin d' évaluer uniquement les defvars. De plus, l'OP utilisant load-fileimplique qu'il veut évaluer l'ensemble du fichier tout en s'assurant que les defvars sont réévalués.
Kaushal Modi
2
Ma propre approche consiste à évaluer-défun lorsque je modifie les valeurs. C'est assez rare pour être utilisable pour moi. YMMV.
YoungFrog

Réponses:

3

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

Cela fonctionnera tant que vous aurez chargé les defvars pour la première fois en chargeant la bibliothèque via emacs, sans l'utiliser eval-defun, eval-bufferetc.

Lorsque vous utilisez require, load-libraryetc. Emacs gardera une trace des variables et des fonctions qui font partie de votre bibliothèque, et les supprimera entièrement pour vous lorsque vous utilisez unload-feature.

Lors de l'écriture de packages, je trouve que l'utilisation du code ci-dessus est une meilleure solution que l'exécution eval-defunlorsque vous écrivez du nouveau code afin de ne pas entrer dans des états intermédiaires.

Jordon Biondo
la source
(info "(elisp) Loading"), (info "(elisp) Unloading")Et unload-featureexiger forcearg si la bibliothèque est la dépendance à une autre bibliothèque. Très bonne réponse! Je me demande quelle version d'Emacs commence à fournir le déchargement ...
gavenkoa
3

defvarne réaffecte pas la valeur d'une variable de la même manière que, disons setqou setf. Une fois qu'une variable a une valeur, defvar ne la touchez pas.

Du defvardocstring de:

(defvar SYMBOL &optional INITVALUE DOCSTRING)

Définissez SYMBOL en tant que variable et renvoyez SYMBOL.

...

L'argument facultatif INITVALUE est évalué et utilisé pour définir SYMBOL, uniquement si la valeur de SYMBOL est nulle. Si SYMBOL est buffer-local, sa valeur par défaut est celle qui est définie; les valeurs locales du tampon ne sont pas affectées. Si INITVALUE est manquant, la valeur de SYMBOL n'est pas définie.

...

Comme vous avez probablement defvarédité les variables en question pour leur donner des valeurs lors du premier chargement de la bibliothèque, le rechargement de la bibliothèque ne changera pas les valeurs.

Voir aussi le nœud manuel elisp sur la définition des variables globales .

Au lieu de vous fier à defvar, vous pouvez toujours réaffecter des valeurs avec setq. Comme alternative, une option maladroite, vous pouvez uninternles symboles afin que les defvars ne les trouvent pas lors du rechargement:

(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
la source
2
Dans ce contexte, c'est-à-dire lors du développement d'un paquet elisp, defvarest la bonne chose à utiliser. setqencombrerait les personnalisations définies par les utilisateurs individuels. OP demande un moyen de forcer l'écrasement des defvarvariables pendant le développement du paquet . Passer à setqnécessiterait de revenir à la defvardate de publication du package.
Tyler
@Tyler, oui, je suis d'accord defvarpour le développement de paquets. Je souligne seulement que defvarcela ne réattribue pas les valeurs, alors que le setqfait.
Dan
2

Essaye ça:

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

Cela utilise simplement le même code que celui eval-defunutilisé sur un defvar. Il traverse le tampon (ou sa restriction en se rétrécissant), s'arrêtant à chacun defvaret utilisant le eval-defuncode dessus.

A dessiné
la source
1

Après avoir entendu qu'il n'y a pas de solution pratique pour réévaluer le tampon avec la réaffectation de defvar, j'ai fait une fonction simple qui repose sur 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))))

Structure de code inspirée de l' eval-defun-2implémentation. Il est similaire à Comment puis-je forcer la réévaluation d'un defvar? Solution.

À l'origine, je veux que la fonction de haut niveau réévalue la bibliothèque qui a été réinstallée via le script de construction afin:

(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 solution Drew fonctionne même sur imbriqué, defvarmais il est difficile de comprendre complètement le code.

Je pense aussi à uninterntous les symboles basés sur le préfixe / l'expression régulière des symboles (comme Dan l'a suggéré) mais je suis paresseux de taper le préfixe à chaque fois ... Voir Comment puis-je dissocier toutes les définitions de symboles avec un certain préfixe?

gavenkoa
la source