从Emacs中的钩子中删除特定lambda的函数

时间:2014-03-26 19:20:20

标签: emacs lambda elisp hook

我找到了这个宏,为特定的项目路径运行代码:

(defmacro project-specifics (name &rest body)
  `(progn
     (add-hook 'find-file-hook
             (lambda ()
               (when (string-match-p ,name (buffer-file-name))
                 ,@body)))
     (add-hook 'dired-after-readin-hook
             (lambda ()
               (when (string-match-p ,name (dired-current-directory))
                 ,@body)))))

我用它:

(project-specifics "projects/test"
  (message "z"))

我正在进行修改,将从钩子中删除prevoius lambda,到目前为止我有辅助函数

(defun remove-lambda-helper (list matcher)
  (dolist (item list)
    (if (and (listp item) (eq (car item) 'lambda))
        (when (funcall matcher item)
          (message "found")
          (setq list (delete item list))))))

(defun remove-hook-name-lambda (name hook)
  (remove-lambda-helper hook
                        (lambda (body)
                          (equal (cadr (cadr (caddr body))) name))))

但是当我打电话时:

(remove-hook-name-lambda "projects/test" find-file-hook)
找到的

显示在*Messages*缓冲区中,但不删除lambda。这里有什么问题?

3 个答案:

答案 0 :(得分:4)

问题

原因可能是找到的对象是list中的 first ,在这种情况下(delete item list)返回(cdr list)而不是修改其结构以保留身份

重点是delete 不能保证

(eq x (delete item x))
==> t

e.g。当itemx的唯一元素时,delete将返回nileq不能cons原始remove-lambda-helper

解决方案

解决方案是通过替换

返回来自(dolist (item list) ...) 的列表的新值
(dolist (item list list) ...)

remove-hook-name-lambda

并在(defun remove-hook-name-lambda (name hook-name) (set hook-name (remove-lambda-helper (symbol-value hook) (lambda (body) (equal (cadr (cadr (caddr body))) name))))) 中使用它,例如add-to-list

(add-hook 'my-hook (lambda () ...))

最后的备注

将lambda添加到钩子中并不是一个好主意,特别是如果你想稍后删除它们。请注意,如果您碰巧编译代码,则lambda删除测试将失败。

如果你修改它们,Lambda也会在钩子中累积,例如,如果你有

lambda

然后您修改add-hook并再次评估defun表单,最终会在两个 lambdas中结束。

更好的解决方案是使用(defmacro add-project-specifics (name &rest body) (let ((ffh (intern (concat name "-find-file-hook"))) (darh (intern (concat name "-dired-after-readin-hook")))) `(progn (defun ,ffh () (when (string-match-p ,name (buffer-file-name)) ,@body)) (add-hook 'find-file-hook ',ffh) (defun ,darh () (when (string-match-p ,name (dired-current-directory)) ,@body)) (add-hook 'dired-after-readin-hook ',darh)))) (defmacro remove-project-specifics (name) (let ((ffh (intern (concat name "-find-file-hook"))) (darh (intern (concat name "-dired-after-readin-hook")))) `(progn (remove-hook 'find-file-hook ',ffh) (unintern ',ffh nil) (remove-hook 'dired-after-readin-hook ',darh) (unintern ',darh nil)))) 来定义函数:

add-project-specifics

PS

回应您在评论中表达的担忧,只要您在与读者打交道时引用它们,符号中的特殊字符就可以了;因为你不打算这样做 - 你只会使用remove-project-specifics.dir-locals.el - 你可以随意实习。

解决实际问题的最佳解决方案

在{{1}}中使用Per-Directory Local Variables

答案 1 :(得分:2)

将lambda添加到钩子和由名称键入的哈希表中。然后当你需要删除它时,在哈希表中查找lambda并从钩子中查找它。

关于Elisp哈希表:http://www.gnu.org/software/emacs/manual/html_node/elisp/Hash-Tables.html

btw(eq(car item)'lambda)在词法绑定时会失败。

答案 2 :(得分:1)

我想提出一个重构,它允许你定义一次钩子,然后让它只在满足某些条件时运行实际动作。

(defvar project-specific-name-regex "projects/test"
  "*Regex to match on buffer name in order to trigger `project-specific-fun'.")
(defvar project-specific-fun (lambda () (message "z"))
  "*Lambda form to run when `project-specific-name-regex' triggers.")

(defsubst project-specific-trigger-maybe (name)
  (and project-specific-name-regex
       (stringp project-specific-name-regex)
       (string-match-p project-specific-name-regex name)
       project-specific-fun
       (funcall project-specific-fun) ) )
(defun project-specific-find-file ()
  "Hook run from `find-file-hook' to run `project-specific-fun' if it's set
and the buffer name matches `project-specific-name-regex'."
  (project-specific-trigger-maybe (buffer-file-name)) )
(defun project-specific-dired-after-readin ()
  "Hook run from `dired-after-readin-hook' to run `project-specific-fun'
if it's set and the directory name matches `project-specific-name-regex'."
  (project-specific-trigger-maybe (dired-current-directory)) )

(add-hook 'find-file-hook #'project-specific-find-file)
(add-hook 'dired-after-readin-hook #'project-specific-dired-after-readin)

因此,只需取消设置project-specific-name和/或project-specific-fun即可禁用挂钩操作。