Консультирование интерактивной функции emacs: до
Я хочу перед советом некоторые функции, которые используют интерактивные аргументы, например find-dired
:
(defadvice find-dired (before eab-find-dired activate)
(message "before!")
(setq find-args '("-iname '**'" . 10)))
Но Emacs выполняет этот совет только после find-dired
интерактивный сеанс, и я не могу настроить find-args
до. Как разрешить противоречие?
Upd. Обратите внимание, что defadvice
макрос устарел.
5 ответов
artscan ответил на его собственный вопрос с artscan ответом, но он немного неполон и вводит в заблуждение. Это также включает в себя 'interactive
, что само по себе может вводить в заблуждение, поскольку выглядит так, как будто оно определено внутри тела команды, но фактически используется до того, как функция введена, и до выполнения какого-либо совета (если этот совет не был 'interactive
звонки...)
В документации для консультаций отсутствует ряд деталей, которые могли бы помочь в этой ситуации, поэтому лучшее место для поиска - это источник: advice.el
, Посмотрите на это и найдите раздел комментариев @ Foo games: An advice tutorial
, Вы также можете найти исходный код в самом Emacs с помощью Mx find-library advice RET.
В частности, для этой проблемы, посмотрите на раздел в advice.el
маркированный @@ Advising interactive behavior:
- потому что это именно то, что вы пытаетесь сделать.
Если вы прочитаете его внимательно, вы заметите, что совет не обязательно должен иметь форму around
, но может быть before
а может и after
- хотя это просто напрашивается на неприятности. Это потому что interactive
это (и должно быть) лечиться особенным.
Итак, следующий код работает (обратите внимание на before
):
(defadvice find-dired (before eab-find-dired (dir args) activate)
"ignore find-args, hard code \"-iname '**'\""
(interactive
(list (read-directory-name "Run find in directory: " nil "" t)
(read-string "Run find (with args): " '("-iname '**'" . 10)
'(find-args-history . 1)))))
Вероятно, более чистый способ сделать это, как предлагали другие, - написать свою собственную функцию, и я думаю, что самый простой ответ Lindydancer.
Совет является довольно заманчивым инструментом, но его легко использовать. Я бы не сказал, что это опасно, но его следует использовать с осторожностью. Кажется, это лучше всего использовать, когда написание вашей собственной функции не работает - например, изменение поведения функции, вызываемой кодом, который вы не можете изменить. Я думаю, что хорошие примеры этой ситуации могут быть найдены здесь, здесь и здесь (чтобы поднять мой собственный рог).
Emacs подхватывает interactive
спецификация перед вызовом функции.
В общем, это плохая идея defadvice
поэтому вместо этого я бы предложил вам определить свою собственную функцию и связать ее с соответствующей клавишей. Например:
(defun my-find-dired ()
(interactive)
(let ((find-args '("-iname '**'" . 10)))
(call-interactively 'find-dired)))
Конечно, вы также можете просто сделать следующее, если считаете, что этот параметр необходим для всех вызовов. find-dired
:
(setq find-args '("-iname '**'" . 10))
Почему вы хотите посоветовать интерактивную функцию?
Вы можете легко определить свою собственную команду
(defun find-dired-my-defaults (dir args)
"just like `find-dired' but with defaults."
(interactive
(list (read-directory-name "Run find in directory: " nil "" t)
(read-string "Run find (with args): " '("-iname '**'" . 1)
'(find-args-history . 1))))
(find-dired dir args))
И если он был связан в раскладке ключей, вы можете легко переназначить его:
(define-key foo-mode-map [remap find-dired] 'find-dired-my-defaults)
Конечный пользователь почти никогда не должен использовать defadvice
несмотря на то, что вики говорит вам.
РЕДАКТИРОВАТЬ: @ Ответ Lindydancer лучше в этом случае, но я оставлю этот ответ здесь, чтобы отговорить будущих читателей от использования defadvice
такими искусственными способами.
Оно работает:
(defadvice find-dired (around eab-find-dired (dir args) activate)
(interactive
(list (read-directory-name "Run find in directory: " nil "" t)
(read-string "Run find (with args): " '("-iname '**'" . 10)
'(find-args-history . 1))))
ad-do-it)
я использую interactive form
функции find-dired
с подстановкой: поставить нужное выражение '("-iname '**'" . 10)
вместо find-args
прямо в форме. around-advice
с аргументами (dir args)
используется вместо before-advice
,
Я столкнулся с этой проблемой в поисках простого способа расширить интерактивное поведение eval-last-sexp
, describe-function
и аналогичные функции без необходимости написания специализированных советов для каждой из них. Для этого я проанализировал стек вызовов интерактивного использования, используя toggle-debug-on-error
и фиктивная функция (defun x () (interactive (list :interactiveform (error "Int"))))
,
- Интерактивное использование всегда предполагает обращение к
call-interactively
, - Когда команда выполняется нажатием горячей клавиши, команда передается
command-execute
, который вызываетcall-interactively
, - Когда выполняется как
M-x COMMAND
имя функции сначала передается в виде строкиexecute-extended-command
(это команда, вызываемаяM-x
), который затем вызываетcommand-execute
...
В зависимости от того, все ли вызовы interactive
форма должна зависеть от совета или только от прямой горячей клавиши или M-x
вызов, можно рекомендовать одну из этих функций, например, используя современные nadvice.el
интерфейс:
(defun setup-var-advice (oldfun command &rest r)
(if (eq command 'save-buffer)
(let ((myvar t)) (apply oldfun command r)) ;; Advice sets up variable
(apply oldfun command r))) ;; Advice has no effect
(advice-add #'call-interactively :around #'setup-var-advice)
В этом простом случае издержки, связанные с функцией рекомендации, невелики, но для более реалистичных ситуаций, вероятно, важно сохранить проверку того, должен ли совет иметь эффект, настолько эффективный, насколько это возможно.