Posso recarregar uma biblioteca e ter valores diferentes para reatribuir?

10

Estou desenvolvendo uma biblioteca e gostaria de recarregá-la após a edição sem sair do Emacs (suponha que esteja ativado load-path):

(load-library "myname")

Quando faço isso, o Emacs não defvarcapta as alterações nas variáveis ​​de saída.

Não quero chamar eval-defun( C-M-x) manualmente em cada formulário de nível superior. Será que M-x eval-buffero respeito defvar/ defcustom?

gavenkoa
fonte
11
Talvez (unload-feature 'myname)primeiro?
npostavs
Apenas tentei e não, ao contrário eval-defundele não capta mudanças defvar.
JeanPierre
11
@KaushalModi: Não acho que seja uma duplicata. Esta questão é sobre atuar em todos os defvars em um arquivo ou buffer, se bem entendi.
Drew
11
Normalmente, nunca seria necessário avaliar apenas os defvars. Além disso, o uso do OP load-fileimplica que ele deseja avaliar o arquivo inteiro e garantir que os defvars sejam reavaliados.
Kaushal Modi
2
Minha própria abordagem é eval-defun à medida que mudo os valores. É pouco frequente para ser útil para mim. YMMV.
YoungFrog

Respostas:

3

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

Isso funcionará desde que você carregue os defvars pela primeira vez carregando a biblioteca no emacs, e não usando eval-defun, eval-bufferetc.

Quando você usa require, load-libraryetc. O Emacs monitora quais variáveis ​​e funções fazem parte da sua biblioteca e as remove totalmente para você quando você usa unload-feature.

Ao escrever pacotes, acho que usar o código acima é uma solução melhor do que executar eval-defunquando você escreve um novo código para não entrar em estados intermediários.

Jordon Biondo
fonte
(info "(elisp) Loading"), (info "(elisp) Unloading")E unload-featureexigem forcearg se biblioteca é de dependência para outra biblioteca. Ótima resposta! Eu estou querendo saber qual é a versão Emacs começar a fornecer descarga ...
gavenkoa
3

defvarnão reatribui o valor de uma variável da mesma maneira que, digamos, setqou setf. Depois que uma variável tiver um valor, defvar ela não será tocada.

Da defvardocumentação de:

(defvar SYMBOL &optional INITVALUE DOCSTRING)

Defina SYMBOL como uma variável e retorne SYMBOL.

...

O argumento opcional INITVALUE é avaliado e usado para definir SYMBOL, apenas se o valor de SYMBOL for nulo. Se SYMBOL for local do buffer, seu valor padrão é o que está definido; valores de buffer-local não são afetados. Se INITVALUE estiver ausente, o valor de SYMBOL não está definido.

...

Como você presumivelmente defvareditou as variáveis ​​em questão para fornecer valores a elas quando você carregou a biblioteca, recarregar a biblioteca não alterará os valores.

Consulte também o nó manual elisp em Definindo variáveis ​​globais .

Em vez de confiar defvar, você sempre pode reatribuir valores com setq. Como opção alternativa e desajeitada, você pode uninternusar os símbolos para que os defvars não os encontrem ao recarregar:

(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
fonte
2
Nesse contexto, ou seja, ao desenvolver um pacote elisp, defvaré a coisa correta a ser usada. setqusaria personalizações definidas por usuários individuais. O OP está pedindo uma maneira de forçar a substituição de defvarvariáveis durante o desenvolvimento do pacote . Mudar para setqexigiria voltar para defvarquando o pacote for lançado.
Tyler
@ Tyler, sim, eu concordo que defvaré apropriado para o desenvolvimento de pacotes. Estou apenas apontando que defvarnão reatribui valores, enquanto o setqfaz.
Dan
2

Tente o seguinte:

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

Isso apenas usa o mesmo código que eval-defunusa em um defvar. Ele atravessa o buffer (ou sua restrição estreitando), parando em cada um defvare usando o eval-defuncódigo nele.

Desenhou
fonte
1

Depois que ouvi dizer que não há uma solução conveniente para a reavaliação do buffer com a reatribuição de defvarfunções que fiz simples, que se baseiam em 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))))

Estrutura de código inspirada na eval-defun-2implementação. É semelhante a Como forço a reavaliação de um defvar? solução.

Originalmente, quero que a função de alto nível reavalie a biblioteca que foi reinstalada por meio do script de construção, para:

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

A solução Drew funciona mesmo em aninhados, defvarmas é difícil entender completamente o código.

Também penso em uninterntodos os símbolos com base no prefixo / regex de símbolos (como Dan sugeriu), mas estou com preguiça de digitar o prefixo toda vez ... Consulte Como posso desassociar todas as definições de símbolos com um determinado prefixo?

gavenkoa
fonte