Могу ли я перезагрузить библиотеку и дать defvar переназначить значения?

10

Я занимаюсь разработкой библиотеки и хотел бы перезагрузить ее после редактирования, не выходя из Emacs (предположим, что она включена load-path):

(load-library "myname")

Когда я делаю это, Emacs не воспринимает изменения связанных defvarпеременных.

Я не хочу вызывать eval-defun( C-M-x) вручную на каждой форме верхнего уровня. Имеет ли M-x eval-bufferуважение defvar/ defcustom?

gavenkoa
источник
1
Может (unload-feature 'myname)сначала?
npostavs
Просто попробовал и нет, в отличие от eval-defunэтого не забрать изменения в defvar.
ЖанПьер
1
@KaushalModi: я не думаю, что это дубликат. Этот вопрос касается действий по всем defvars в файле или буфере, если я правильно понимаю.
Дрю
1
Обычно никогда не нужно оценивать только defvars. Также использование OP load-fileподразумевает, что он хочет оценить весь файл, убедившись, что defvars переоценены.
Каушал Моди
2
Мой собственный подход - eval-defun, когда я меняю значения. Это достаточно редко, чтобы быть полезным для меня. YMMV.
YoungFrog

Ответы:

3

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

Это будет работать до тех пор, пока вы сначала загрузили defvars, загрузив библиотеку через emacs, не используя eval-defunи eval-bufferт.д.

Когда вы используете requireи load-libraryт. Д. Emacs будет отслеживать, какие переменные и функции являются частью вашей библиотеки, и полностью удалит их при использовании unload-feature.

При написании пакетов я обнаружил, что использование приведенного выше кода является лучшим решением, чем выполнение, eval-defunкогда вы пишете новый код, чтобы не попасть в промежуточные состояния.

Джордон Биондо
источник
(info "(elisp) Loading"), (info "(elisp) Unloading")И unload-featureтребуют forceArg , если библиотека зависимости для другой библиотеки. Отличный ответ! Мне интересно, какую версию Emacs начинают обеспечивать разгрузку ...
gavenkoa
3

defvarне переназначает значение переменной так же, как, скажем, setqили setf. Как только переменная имеет значение, defvar не будет ее трогать.

Из defvarдокументации:

(defvar SYMBOL &optional INITVALUE DOCSTRING)

Определите SYMBOL как переменную и верните SYMBOL.

...

Необязательный аргумент INITVALUE оценивается и используется для установки SYMBOL, только если значение SYMBOL равно void. Если SYMBOL является локальным по отношению к буферу, его значением по умолчанию является то, что установлено; локальные значения буфера не затрагиваются. Если значение INITVALUE отсутствует, значение SYMBOL не устанавливается.

...

Поскольку вы предположительно defvarредактировали эти переменные, чтобы дать им значения при первой загрузке библиотеки, повторная загрузка библиотеки не изменит значения.

См. Также узел руководства elisp по определению глобальных переменных .

Вместо того чтобы полагаться defvar, вы всегда можете переназначить значения с помощью setq. В качестве альтернативы, неуклюжий вариант, вы можете uninternсимволы, чтобы defvarони не нашли их при перезагрузке:

(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"
Дэн
источник
2
В этом контексте, то есть при разработке пакета elisp, defvarэто правильная вещь для использования. setqможет изменить настройки, заданные отдельными пользователями. OP запрашивает способ принудительной перезаписи defvarпеременных во время разработки пакета . Переключение на setqпотребует возврата обратно к defvarмоменту выпуска пакета.
Тайлер
@ Тайлер, да, я согласен, что defvarподходит для разработки пакетов. Я только указываю, что defvarне переназначает значения, пока setqделает.
Дан
2

Попробуй это:

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

Это просто использует тот же код, который eval-defunиспользуется на defvar. Он пересекает буфер (или его ограничение сужением), останавливаясь на каждом defvarи используя eval-defunкод на нем.

Нарисовалась
источник
1

После того, как я узнал, что нет удобного решения для переоценки буфера с переназначением defvar, я сделал простую функцию, которая включает 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))))

Структура кода вдохновлена eval-defun-2реализацией. Это похоже на Как я могу принудительно пересмотреть defvar? решение.

Первоначально я хочу, чтобы функция высокого уровня переоценила библиотеку, которая была переустановлена ​​с помощью скрипта сборки, так:

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

Решение Дрю работает даже на вложенных, defvarно трудно полностью понять код.

Я также думаю обо uninternвсех символах, основанных на префиксе / регулярном выражении символов (как предложил Дэн ), но мне лень набирать префикс каждый раз ... См. Как я могу отменить привязку всех определений символов с определенным префиксом?

gavenkoa
источник